summaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/alsa/Makefile.am1
-rw-r--r--backends/alsa/alsa-backend.c333
-rw-r--r--backends/alsa/alsa-constants.c189
-rw-r--r--backends/alsa/alsa-constants.h21
-rw-r--r--backends/alsa/alsa-device.c690
-rw-r--r--backends/alsa/alsa-device.h4
-rw-r--r--backends/alsa/alsa-element.c16
-rw-r--r--backends/alsa/alsa-element.h3
-rw-r--r--backends/alsa/alsa-stream-control.c110
-rw-r--r--backends/alsa/alsa-stream-control.h6
-rw-r--r--backends/alsa/alsa-stream-input-control.c12
-rw-r--r--backends/alsa/alsa-stream-input-control.h4
-rw-r--r--backends/alsa/alsa-stream-output-control.c12
-rw-r--r--backends/alsa/alsa-stream-output-control.h4
-rw-r--r--backends/alsa/alsa-stream.c335
-rw-r--r--backends/alsa/alsa-stream.h41
-rw-r--r--backends/alsa/alsa-switch-option.c3
-rw-r--r--backends/alsa/alsa-switch-option.h12
-rw-r--r--backends/alsa/alsa-switch.c47
-rw-r--r--backends/alsa/alsa-switch.h7
-rw-r--r--backends/alsa/alsa-toggle.c37
-rw-r--r--backends/alsa/alsa-toggle.h11
-rw-r--r--backends/null/Makefile.am1
-rw-r--r--backends/null/null-backend.c3
-rw-r--r--backends/oss/Makefile.am8
-rw-r--r--backends/oss/oss-backend.c367
-rw-r--r--backends/oss/oss-backend.h8
-rw-r--r--backends/oss/oss-device.c665
-rw-r--r--backends/oss/oss-device.h12
-rw-r--r--backends/oss/oss-stream-control.c200
-rw-r--r--backends/oss/oss-stream-control.h23
-rw-r--r--backends/oss/oss-stream.c251
-rw-r--r--backends/oss/oss-stream.h32
-rw-r--r--backends/oss/oss-switch-option.c72
-rw-r--r--backends/oss/oss-switch-option.h69
-rw-r--r--backends/oss/oss-switch.c203
-rw-r--r--backends/oss/oss-switch.h70
-rw-r--r--backends/oss/oss-types.h32
-rw-r--r--backends/pulse/Makefile.am24
-rw-r--r--backends/pulse/pulse-backend.c1078
-rw-r--r--backends/pulse/pulse-backend.h9
-rw-r--r--backends/pulse/pulse-client-stream.c433
-rw-r--r--backends/pulse/pulse-client-stream.h94
-rw-r--r--backends/pulse/pulse-connection.c39
-rw-r--r--backends/pulse/pulse-connection.h5
-rw-r--r--backends/pulse/pulse-device-profile.c88
-rw-r--r--backends/pulse/pulse-device-profile.h69
-rw-r--r--backends/pulse/pulse-device-switch.c261
-rw-r--r--backends/pulse/pulse-device-switch.h77
-rw-r--r--backends/pulse/pulse-device.c575
-rw-r--r--backends/pulse/pulse-device.h33
-rw-r--r--backends/pulse/pulse-ext-stream.c374
-rw-r--r--backends/pulse/pulse-ext-stream.h30
-rw-r--r--backends/pulse/pulse-helpers.c27
-rw-r--r--backends/pulse/pulse-helpers.h9
-rw-r--r--backends/pulse/pulse-monitor.c52
-rw-r--r--backends/pulse/pulse-monitor.h10
-rw-r--r--backends/pulse/pulse-port-switch.c241
-rw-r--r--backends/pulse/pulse-port-switch.h77
-rw-r--r--backends/pulse/pulse-port.c89
-rw-r--r--backends/pulse/pulse-port.h70
-rw-r--r--backends/pulse/pulse-sink-control.c161
-rw-r--r--backends/pulse/pulse-sink-control.h67
-rw-r--r--backends/pulse/pulse-sink-input.c321
-rw-r--r--backends/pulse/pulse-sink-input.h22
-rw-r--r--backends/pulse/pulse-sink-switch.c76
-rw-r--r--backends/pulse/pulse-sink-switch.h62
-rw-r--r--backends/pulse/pulse-sink.c382
-rw-r--r--backends/pulse/pulse-sink.h24
-rw-r--r--backends/pulse/pulse-source-control.c154
-rw-r--r--backends/pulse/pulse-source-control.h67
-rw-r--r--backends/pulse/pulse-source-output.c306
-rw-r--r--backends/pulse/pulse-source-output.h37
-rw-r--r--backends/pulse/pulse-source-switch.c76
-rw-r--r--backends/pulse/pulse-source-switch.h62
-rw-r--r--backends/pulse/pulse-source.c339
-rw-r--r--backends/pulse/pulse-source.h27
-rw-r--r--backends/pulse/pulse-stream-control.c744
-rw-r--r--backends/pulse/pulse-stream-control.h94
-rw-r--r--backends/pulse/pulse-stream.c1225
-rw-r--r--backends/pulse/pulse-stream.h64
-rw-r--r--backends/pulse/pulse-types.h45
82 files changed, 7088 insertions, 4945 deletions
diff --git a/backends/alsa/Makefile.am b/backends/alsa/Makefile.am
index 220bb3b..48dcaba 100644
--- a/backends/alsa/Makefile.am
+++ b/backends/alsa/Makefile.am
@@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer
backend_LTLIBRARIES = libmatemixer-alsa.la
AM_CPPFLAGS = \
+ -Wno-unknown-pragmas \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"libmatemixer-alsa\"
diff --git a/backends/alsa/alsa-backend.c b/backends/alsa/alsa-backend.c
index 7a17b85..6bac691 100644
--- a/backends/alsa/alsa-backend.c
+++ b/backends/alsa/alsa-backend.c
@@ -27,12 +27,22 @@
#include "alsa-stream.h"
#define BACKEND_NAME "ALSA"
-#define BACKEND_PRIORITY 9
+#define BACKEND_PRIORITY 20
+
+#define ALSA_DEVICE_GET_ID(d) \
+ (g_object_get_data (G_OBJECT (d), "__matemixer_alsa_device_id"))
+
+#define ALSA_DEVICE_SET_ID(d,id) \
+ (g_object_set_data_full (G_OBJECT (d), \
+ "__matemixer_alsa_device_id", \
+ g_strdup (id), \
+ g_free))
struct _AlsaBackendPrivate
{
GSource *timeout_source;
- GHashTable *devices;
+ GList *streams;
+ GList *devices;
GHashTable *devices_ids;
};
@@ -47,26 +57,38 @@ static void alsa_backend_finalize (GObject *object);
G_DEFINE_DYNAMIC_TYPE (AlsaBackend, alsa_backend, MATE_MIXER_TYPE_BACKEND)
#pragma clang diagnostic pop
-static gboolean alsa_backend_open (MateMixerBackend *backend);
-static void alsa_backend_close (MateMixerBackend *backend);
-static GList * alsa_backend_list_devices (MateMixerBackend *backend);
-static GList * alsa_backend_list_streams (MateMixerBackend *backend);
+static gboolean alsa_backend_open (MateMixerBackend *backend);
+static void alsa_backend_close (MateMixerBackend *backend);
+static const GList *alsa_backend_list_devices (MateMixerBackend *backend);
+static const GList *alsa_backend_list_streams (MateMixerBackend *backend);
+
+static gboolean read_devices (AlsaBackend *alsa);
+
+static gboolean read_device (AlsaBackend *alsa,
+ const gchar *card);
+
+static void add_device (AlsaBackend *alsa,
+ AlsaDevice *device);
-static gboolean read_devices (AlsaBackend *alsa);
+static void remove_device (AlsaBackend *alsa,
+ AlsaDevice *device);
+static void remove_device_by_name (AlsaBackend *alsa,
+ const gchar *name);
+static void remove_device_by_list_item (AlsaBackend *alsa,
+ GList *item);
-static gboolean read_device (AlsaBackend *alsa,
- const gchar *card);
+static void remove_stream (AlsaBackend *alsa,
+ const gchar *name);
-static void add_device (AlsaBackend *alsa,
- AlsaDevice *device);
+static void select_default_input_stream (AlsaBackend *alsa);
+static void select_default_output_stream (AlsaBackend *alsa);
-static void remove_device (AlsaBackend *alsa,
- AlsaDevice *device);
-static void remove_stream (AlsaBackend *alsa,
- const gchar *name);
+static void free_stream_list (AlsaBackend *alsa);
-static void select_default_input_stream (AlsaBackend *alsa);
-static void select_default_output_stream (AlsaBackend *alsa);
+static gint compare_devices (gconstpointer a,
+ gconstpointer b);
+static gint compare_device_name (gconstpointer a,
+ gconstpointer b);
static MateMixerBackendInfo info;
@@ -118,11 +140,6 @@ alsa_backend_init (AlsaBackend *alsa)
ALSA_TYPE_BACKEND,
AlsaBackendPrivate);
- alsa->priv->devices = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
alsa->priv->devices_ids = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
@@ -151,7 +168,6 @@ alsa_backend_finalize (GObject *object)
alsa = ALSA_BACKEND (object);
- g_hash_table_unref (alsa->priv->devices);
g_hash_table_unref (alsa->priv->devices_ids);
G_OBJECT_CLASS (alsa_backend_parent_class)->finalize (object);
@@ -166,9 +182,8 @@ alsa_backend_open (MateMixerBackend *backend)
alsa = ALSA_BACKEND (backend);
- /* Poll ALSA for changes every 500 milliseconds, this actually only
- * discovers added or changed sound cards, sound card related events
- * are handled by AlsaDevices */
+ /* Poll ALSA for changes every second, this only discovers added or removed
+ * sound cards, sound card related events are handled by AlsaDevices */
alsa->priv->timeout_source = g_timeout_source_new_seconds (1);
g_source_set_callback (alsa->priv->timeout_source,
(GSourceFunc) read_devices,
@@ -197,61 +212,60 @@ alsa_backend_close (MateMixerBackend *backend)
g_source_destroy (alsa->priv->timeout_source);
- g_hash_table_remove_all (alsa->priv->devices);
+ if (alsa->priv->devices != NULL) {
+ g_list_free_full (alsa->priv->devices, g_object_unref);
+ alsa->priv->devices = NULL;
+ }
+
+ free_stream_list (alsa);
+
g_hash_table_remove_all (alsa->priv->devices_ids);
_mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE);
}
-static GList *
+static const GList *
alsa_backend_list_devices (MateMixerBackend *backend)
{
- GList *list;
-
g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_BACKEND (backend)->priv->devices);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return ALSA_BACKEND (backend)->priv->devices;
}
-static GList *
+static const GList *
alsa_backend_list_streams (MateMixerBackend *backend)
{
- AlsaBackend *alsa;
- GHashTableIter iter;
- gpointer value;
- GList *list = NULL;
+ AlsaBackend *alsa;
g_return_val_if_fail (ALSA_IS_BACKEND (backend), NULL);
alsa = ALSA_BACKEND (backend);
- /* We don't keep a list or hash table of all streams here, instead walk
- * through the list of devices and create the list manually, each device
- * has at most one input and one output stream */
- g_hash_table_iter_init (&iter, alsa->priv->devices);
-
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- AlsaDevice *device = ALSA_DEVICE (value);
- AlsaStream *stream;
-
- stream = alsa_device_get_output_stream (device);
- if (stream != NULL)
- list = g_list_prepend (list, stream);
- stream = alsa_device_get_input_stream (device);
- if (stream != NULL)
- list = g_list_prepend (list, stream);
+ if (alsa->priv->streams == NULL) {
+ GList *list;
+
+ /* Walk through the list of devices and create the stream list, each
+ * device has at most one input and one output stream */
+ list = alsa->priv->devices;
+
+ while (list != NULL) {
+ AlsaDevice *device = ALSA_DEVICE (list->data);
+ AlsaStream *stream;
+
+ stream = alsa_device_get_input_stream (device);
+ if (stream != NULL) {
+ alsa->priv->streams =
+ g_list_append (alsa->priv->streams, g_object_ref (stream));
+ }
+ stream = alsa_device_get_output_stream (device);
+ if (stream != NULL) {
+ alsa->priv->streams =
+ g_list_append (alsa->priv->streams, g_object_ref (stream));
+ }
+ list = list->next;
+ }
}
-
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return alsa->priv->streams;
}
static gboolean
@@ -260,12 +274,12 @@ read_devices (AlsaBackend *alsa)
gint num;
gint ret;
gchar card[16];
- gboolean changed = FALSE;
+ gboolean added = FALSE;
/* Read the default device first, it will be either one of the hardware cards
* that will be queried later, or a software mixer */
if (read_device (alsa, "default") == TRUE)
- changed = TRUE;
+ added = TRUE;
for (num = -1;;) {
/* Read number of the next sound card */
@@ -277,12 +291,12 @@ read_devices (AlsaBackend *alsa)
g_snprintf (card, sizeof (card), "hw:%d", num);
if (read_device (alsa, card) == TRUE)
- changed = TRUE;
+ added = TRUE;
}
/* If any card has been added, make sure we have the most suitable default
* input and output streams */
- if (changed == TRUE) {
+ if (added == TRUE) {
select_default_input_stream (alsa);
select_default_output_stream (alsa);
}
@@ -300,15 +314,13 @@ read_device (AlsaBackend *alsa, const gchar *card)
/* The device may be already known, remove it if it's known and fails
* to be read, this happens for example when PulseAudio is killed */
- device = g_hash_table_lookup (alsa->priv->devices, card);
-
ret = snd_ctl_open (&ctl, card, 0);
if (ret < 0) {
g_warning ("Failed to open ALSA control for %s: %s",
card,
snd_strerror (ret));
- if (device != NULL)
- remove_device (alsa, device);
+
+ remove_device_by_name (alsa, card);
return FALSE;
}
@@ -317,9 +329,8 @@ read_device (AlsaBackend *alsa, const gchar *card)
ret = snd_ctl_card_info (ctl, info);
if (ret < 0) {
g_warning ("Failed to read card info: %s", snd_strerror (ret));
- if (device != NULL)
- remove_device (alsa, device);
+ remove_device_by_name (alsa, card);
snd_ctl_close (ctl);
return FALSE;
}
@@ -342,11 +353,7 @@ read_device (AlsaBackend *alsa, const gchar *card)
return FALSE;
}
- g_object_set_data_full (G_OBJECT (device),
- "__matemixer_alsa_device_id",
- g_strdup (id),
- g_free);
-
+ ALSA_DEVICE_SET_ID (device, id);
add_device (alsa, device);
snd_ctl_close (ctl);
@@ -356,19 +363,13 @@ read_device (AlsaBackend *alsa, const gchar *card)
static void
add_device (AlsaBackend *alsa, AlsaDevice *device)
{
- const gchar *name;
-
- name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+ alsa->priv->devices = g_list_insert_sorted_with_data (alsa->priv->devices,
+ device,
+ (GCompareDataFunc) compare_devices,
+ NULL);
- g_hash_table_insert (alsa->priv->devices,
- g_strdup (name),
- g_object_ref (device));
-
- /* Remember the device identifier, use a single string copy as we only free
- * the hash table key */
- g_hash_table_add (alsa->priv->devices_ids,
- g_strdup (g_object_get_data (G_OBJECT (device),
- "__matemixer_alsa_device_id")));
+ /* Keep track of device identifiers */
+ g_hash_table_add (alsa->priv->devices_ids, g_strdup (ALSA_DEVICE_GET_ID (device)));
g_signal_connect_swapped (G_OBJECT (device),
"closed",
@@ -379,7 +380,22 @@ add_device (AlsaBackend *alsa, AlsaDevice *device)
G_CALLBACK (remove_stream),
alsa);
- g_signal_emit_by_name (G_OBJECT (alsa), "device-added", name);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "closed",
+ G_CALLBACK (free_stream_list),
+ alsa);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "stream-added",
+ G_CALLBACK (free_stream_list),
+ alsa);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "stream-removed",
+ G_CALLBACK (free_stream_list),
+ alsa);
+
+ g_signal_emit_by_name (G_OBJECT (alsa),
+ "device-added",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
/* Load the device elements after emitting device-added, because the load
* function will most likely emit stream-added on the device and backend */
@@ -389,27 +405,48 @@ add_device (AlsaBackend *alsa, AlsaDevice *device)
static void
remove_device (AlsaBackend *alsa, AlsaDevice *device)
{
- const gchar *name;
+ GList *item;
+
+ item = g_list_find (alsa->priv->devices, device);
+ if (item != NULL)
+ remove_device_by_list_item (alsa, item);
+}
+
+static void
+remove_device_by_name (AlsaBackend *alsa, const gchar *name)
+{
+ GList *item;
+
+ item = g_list_find_custom (alsa->priv->devices, name, compare_device_name);
+ if (item != NULL)
+ remove_device_by_list_item (alsa, item);
+}
+
+static void
+remove_device_by_list_item (AlsaBackend *alsa, GList *item)
+{
+ AlsaDevice *device;
+
+ device = ALSA_DEVICE (item->data);
- name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+ g_signal_handlers_disconnect_by_data (G_OBJECT (device), alsa);
- g_signal_handlers_disconnect_by_func (G_OBJECT (device),
- G_CALLBACK (remove_device),
- alsa);
- g_signal_handlers_disconnect_by_func (G_OBJECT (device),
- G_CALLBACK (remove_stream),
- alsa);
+ if (alsa_device_is_open (device) == TRUE)
+ alsa_device_close (device);
+
+ alsa->priv->devices = g_list_delete_link (alsa->priv->devices, item);
- /* Remove the device */
g_hash_table_remove (alsa->priv->devices_ids,
- g_object_get_data (G_OBJECT (device),
- "__matemixer_alsa_device_id"));
+ ALSA_DEVICE_GET_ID (device));
+
+ /* The list may and may not have been invalidate by device signals */
+ free_stream_list (alsa);
- // XXX close the device and make it remove streams
- g_hash_table_remove (alsa->priv->devices, name);
g_signal_emit_by_name (G_OBJECT (alsa),
"device-removed",
- name);
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+
+ g_object_unref (device);
}
static void
@@ -419,7 +456,6 @@ remove_stream (AlsaBackend *alsa, const gchar *name)
stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (alsa));
- // XXX see if the change happens after stream is removed or before
if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0)
select_default_input_stream (alsa);
@@ -432,36 +468,19 @@ remove_stream (AlsaBackend *alsa, const gchar *name)
static void
select_default_input_stream (AlsaBackend *alsa)
{
- AlsaDevice *device;
- AlsaStream *stream;
- gchar card[16];
- gint num;
-
- /* Always prefer stream in the "default" device */
- device = g_hash_table_lookup (alsa->priv->devices, "default");
- if (device != NULL) {
- stream = alsa_device_get_input_stream (device);
- if (stream != NULL) {
- _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa),
- MATE_MIXER_STREAM (stream));
- return;
- }
- }
+ GList *list;
- /* If there is no input stream in the default device, search the cards in
- * the correct order */
- for (num = 0;; num++) {
- g_snprintf (card, sizeof (card), "hw:%d", num);
+ list = alsa->priv->devices;
+ while (list != NULL) {
+ AlsaDevice *device = ALSA_DEVICE (list->data);
+ AlsaStream *stream = alsa_device_get_input_stream (device);
- device = g_hash_table_lookup (alsa->priv->devices, card);
- if (device == NULL)
- break;
- stream = alsa_device_get_input_stream (device);
if (stream != NULL) {
_mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (alsa),
MATE_MIXER_STREAM (stream));
return;
}
+ list = list->next;
}
/* In the worst case unset the default stream */
@@ -471,38 +490,50 @@ select_default_input_stream (AlsaBackend *alsa)
static void
select_default_output_stream (AlsaBackend *alsa)
{
- AlsaDevice *device;
- AlsaStream *stream;
- gchar card[16];
- gint num;
-
- /* Always prefer stream in the "default" device */
- device = g_hash_table_lookup (alsa->priv->devices, "default");
- if (device != NULL) {
- stream = alsa_device_get_output_stream (device);
- if (stream != NULL) {
- _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa),
- MATE_MIXER_STREAM (stream));
- return;
- }
- }
+ GList *list;
- /* If there is no input stream in the default device, search the cards in
- * the correct order */
- for (num = 0;; num++) {
- g_snprintf (card, sizeof (card), "hw:%d", num);
+ list = alsa->priv->devices;
+ while (list != NULL) {
+ AlsaDevice *device = ALSA_DEVICE (list->data);
+ AlsaStream *stream = alsa_device_get_output_stream (device);
- device = g_hash_table_lookup (alsa->priv->devices, card);
- if (device == NULL)
- break;
- stream = alsa_device_get_output_stream (device);
if (stream != NULL) {
_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa),
MATE_MIXER_STREAM (stream));
return;
}
+ list = list->next;
}
/* In the worst case unset the default stream */
_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (alsa), NULL);
}
+
+static void
+free_stream_list (AlsaBackend *alsa)
+{
+ if (alsa->priv->streams == NULL)
+ return;
+
+ g_list_free_full (alsa->priv->streams, g_object_unref);
+
+ alsa->priv->streams = NULL;
+}
+
+static gint
+compare_devices (gconstpointer a, gconstpointer b)
+{
+ MateMixerDevice *d1 = MATE_MIXER_DEVICE (a);
+ MateMixerDevice *d2 = MATE_MIXER_DEVICE (b);
+
+ return strcmp (mate_mixer_device_get_name (d1), mate_mixer_device_get_name (d2));
+}
+
+static gint
+compare_device_name (gconstpointer a, gconstpointer b)
+{
+ MateMixerDevice *device = MATE_MIXER_DEVICE (a);
+ const gchar *name = (const gchar *) b;
+
+ return strcmp (mate_mixer_device_get_name (device), name);
+}
diff --git a/backends/alsa/alsa-constants.c b/backends/alsa/alsa-constants.c
index 2124a2e..7b7021f 100644
--- a/backends/alsa/alsa-constants.c
+++ b/backends/alsa/alsa-constants.c
@@ -22,15 +22,190 @@
#include "alsa-constants.h"
-// XXX add more and probably move them somewhere else
+/*
+ * These lists of ALSA mixer elements are based on PulseAudio's mixer paths and own
+ * observations. The intention is to provide translatable and in some cases better
+ * readable labels and role assignments. The controls list is also used for selecting
+ * the default controls and the selection machanism relies on the order of elements,
+ * so more useful elements should be placed on the top. The last two boolean values
+ * indicate whether we prefer the element to be used as a default input or output
+ * control.
+ *
+ * Of course the lists are very incomplete and it would be great if users validated and
+ * refreshed them from time to time.
+ */
const AlsaControlInfo alsa_controls[] =
{
- { "Master", N_("Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
- { "Speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
- { "Capture", N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER },
- { "PCM", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM },
- { "Line", N_("Line"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT },
- { "Mic", N_("Mic"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT },
+ /* Output controls */
+ { "Master", N_("Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "Hardware Master", N_("Hardware Master"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "PCM", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, FALSE, TRUE },
+ { "Desktop Speaker", N_("Desktop Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Front", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Front Speaker", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker Front", N_("Front Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Headphone", N_("Headphone"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Headphone2", N_("Headphone 2"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Headset", N_("Headset"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Master Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Surround Speaker", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker Surround", N_("Surround Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Center", N_("Center Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Center Speaker", N_("Center Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "CLFE", N_("CLFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Speaker CLFE", N_("CLFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Master Mono", N_("Master Mono"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "Master Digital", N_("Master Digital"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, FALSE, TRUE },
+ { "Digital/SPDIF", N_("Digital"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, FALSE, TRUE },
+ { "Speaker Side", N_("Side Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Side", N_("Side Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Rear", N_("Rear Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, TRUE },
+ { "Wave", N_("Wave"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE },
+ { "Phone", N_("Phone"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE },
+ { "CD", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, FALSE, TRUE },
+ { "Music", N_("Music"), MATE_MIXER_STREAM_CONTROL_ROLE_MUSIC, FALSE, TRUE },
+ { "AC97", N_("AC97"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, TRUE },
+ { "LFE", N_("LFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "LFE Speaker", N_("LFE Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "Bass Speaker", N_("Bass Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "PC Speaker", N_("PC Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, FALSE, FALSE },
+ { "Synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE },
+ { "MIDI", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE },
+ { "Synth/MIDI", N_("Synth/MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, FALSE, FALSE },
+ { "Bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, FALSE, FALSE },
+ { "Treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, FALSE, FALSE },
+
+ /* Input controls */
+ { "Capture", N_("Capture"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, TRUE, FALSE },
+ { "Mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Mic/Line", N_("Microphone/Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE },
+ { "Internal Mic", N_("Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Int Mic", N_("Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Front Mic", N_("Front Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Rear Mic", N_("Rear Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Dock Mic", N_("Dock Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Headphone Mic", N_("Headphone Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Headset Mic", N_("Headset Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Inverted Internal Mic", N_("Inverted Internal Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, TRUE, FALSE },
+ { "Line", N_("Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE },
+ { "Aux", N_("Auxiliary"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, TRUE, FALSE },
+ { "Video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO, TRUE, FALSE },
+ { "TV Tuner", N_("TV Tuner"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, TRUE, FALSE },
+ { "FM", N_("FM"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, TRUE, FALSE },
+ { "Mic Boost", N_("Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Mic Boost (+20dB)", N_("Microphone Boost (+20dB)"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Int Mic Boost", N_("Internal Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Internal Mic Boost", N_("Internal Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Front Mic Boost", N_("Front Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Rear Mic Boost", N_("Rear Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Dock Mic Boost", N_("Dock Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Headphone Mic Boost", N_("Headphone Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Headset Mic Boost", N_("Headset Microphone Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { "Line Boost", N_("Line In Boost"), MATE_MIXER_STREAM_CONTROL_ROLE_BOOST, FALSE, FALSE },
+ { NULL }
+};
+
+/* Switches and toggles */
+const AlsaSwitchInfo alsa_switches[] =
+{
+ { "Analog Output", N_("Analog Output"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Analog Source", N_("Analog Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Capture Source", N_("Capture Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input Source", N_("Input Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input Source Select", N_("Input Source Select"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Digital Input Source", N_("Digital Input Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "PCM Capture Source", N_("PCM Capture Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "IEC958 Playback Source", N_("Digital Playback Source"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Mono Output Select", N_("Mono Output Select"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Shared Mic/Line in", N_("Shared Mic/Line In"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Mic Select", N_("Microphone Select"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Mic Jack Mode", N_("Microphone Jack Mode"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Surround Jack Mode", N_("Surround Jack Mode"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Auto-Mute Mode", N_("Auto-Mute Mode"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+
+ /* (Probably) toggles */
+ { "External Amplifier", N_("External Amplifier"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Bass Boost", N_("Bass Boost"), MATE_MIXER_SWITCH_ROLE_BOOST },
+ { "Capture Boost", N_("Capture Boost"), MATE_MIXER_SWITCH_ROLE_BOOST },
+ { "IEC958", N_("Digital"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "IEC958 In", N_("Digital In"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "IEC958 Optical Raw", N_("Optical"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Auto Gain Control", N_("Auto Gain Control"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Mix", N_("Mix"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Mix Mono", N_("Mix Mono"), MATE_MIXER_SWITCH_ROLE_UNKNOWN },
+ { "Mic Capture", N_("Mic Capture"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input 1", N_("Input 1"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { "Input 2", N_("Input 2"), MATE_MIXER_SWITCH_ROLE_PORT },
+ { NULL }
+};
+
+const AlsaSwitchOptionInfo alsa_switch_options[] =
+{
+ /* Output options */
+ { "Speakers", N_("Speakers"), NULL },
+ { "Headphones", N_("Headphones"), NULL },
+ { "FP Headphones", N_("Front Panel Headphones"), NULL },
+
+ /* Microphone options */
+ { "Mic", N_("Microphone"), "audio-input-microphone" },
+ { "Microphone", N_("Microphone"), "audio-input-microphone" },
+ { "Mic1", N_("Microphone 1"), "audio-input-microphone" },
+ { "Mic2", N_("Microphone 2"), "audio-input-microphone" },
+ { "Mic in", N_("Microphone In"), "audio-input-microphone" },
+ { "Mic In", N_("Microphone In"), "audio-input-microphone" },
+ { "Front Mic", N_("Front Microphone"), "audio-input-microphone" },
+ { "Front Microphone", N_("Front Microphone"), "audio-input-microphone" },
+ { "Headphone Mic", N_("Headphone Microphone"), "audio-input-microphone" },
+ { "Headset Mic", N_("Headset Microphone"), "audio-input-microphone" },
+ { "Dock Mic", N_("Dock Microphone"), "audio-input-microphone" },
+ { "Internal Mic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "Int Mic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "Internal Mic 1", N_("Internal Microphone 1"), "audio-input-microphone" },
+ { "iMic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "i-Mic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "IntMic", N_("Internal Microphone"), "audio-input-microphone" },
+ { "Int DMic", N_("Internal Digital Microphone"), "audio-input-microphone" },
+ { "Digital Mic", N_("Digital Microphone"), "audio-input-microphone" },
+ { "Digital Mic 1", N_("Digital Microphone 1"), "audio-input-microphone" },
+ { "Digital Mic 2", N_("Digital Microphone 2"), "audio-input-microphone" },
+ { "D-Mic", N_("Digital Microphone"), "audio-input-microphone" },
+ { "ExtMic", N_("External Microphone"), "audio-input-microphone" },
+ { "Ext Mic", N_("External Microphone"), "audio-input-microphone" },
+ { "E-Mic", N_("External Microphone"), "audio-input-microphone" },
+ { "e-Mic", N_("External Microphone"), "audio-input-microphone" },
+ { "Rear Mic", N_("Rear Microphone"), "audio-input-microphone" },
+ { "Cam Mic", N_("Camera Microphone"), "audio-input-microphone" },
+
+ /* Other options */
+ { "Analog", N_("Analog"), NULL },
+ { "Analog In", N_("Analog In"), NULL },
+ { "Analog Inputs", N_("Analog Inputs"), NULL },
+ { "Line in", N_("Line In"), NULL },
+ { "Line In", N_("Line In"), NULL },
+ { "Line-In", N_("Line In"), NULL },
+ { "Mic/Line", N_("Microphone/Line In"), NULL },
+ { "Line/Mic", N_("Line In/Microphone"), NULL },
+ { "LineIn", N_("Line In"), NULL },
+ { "Line", N_("Line In"), NULL },
+ { "Input1", N_("Input 1"), NULL },
+ { "Input2", N_("Input 2"), NULL },
+ { "IEC958 In", N_("Digital In"), NULL },
+ { "TV Tuner", N_("TV Tuner"), NULL },
+ { "FM", N_("FM"), NULL },
+ { "AUX", N_("Auxiliary"), NULL },
+ { "AUX IN", N_("Auxiliary In"), NULL },
+ { "Aux In", N_("Auxiliary In"), NULL },
+ { "Aux", N_("Auxiliary"), NULL },
+ { "Aux0", N_("Auxiliary 0"), NULL },
+ { "Aux1", N_("Auxiliary 1"), NULL },
+ { "Aux2", N_("Auxiliary 2"), NULL },
+ { "Aux3", N_("Auxiliary 3"), NULL },
+ { "Docking-Station", N_("Docking Station"), NULL },
+ { "Mixer", N_("Mixer"), NULL },
+ { "Unknown1", N_("Unknown 1"), NULL },
+ { "Unknown2", N_("Unknown 2"), NULL },
{ NULL }
};
diff --git a/backends/alsa/alsa-constants.h b/backends/alsa/alsa-constants.h
index 81257c7..8137289 100644
--- a/backends/alsa/alsa-constants.h
+++ b/backends/alsa/alsa-constants.h
@@ -22,14 +22,35 @@
#include <alsa/asoundlib.h>
#include <libmatemixer/matemixer.h>
+G_BEGIN_DECLS
+
typedef struct {
gchar *name;
gchar *label;
MateMixerStreamControlRole role;
+ gboolean use_default_input;
+ gboolean use_default_output;
} AlsaControlInfo;
+typedef struct {
+ gchar *name;
+ gchar *label;
+ MateMixerSwitchRole role;
+} AlsaSwitchInfo;
+
+typedef struct {
+ gchar *name;
+ gchar *label;
+ gchar *icon;
+} AlsaSwitchOptionInfo;
+
extern const AlsaControlInfo alsa_controls[];
+extern const AlsaSwitchInfo alsa_switches[];
+extern const AlsaSwitchOptionInfo alsa_switch_options[];
+
extern const MateMixerChannelPosition alsa_channel_map_from[];
extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[];
+G_END_DECLS
+
#endif /* ALSA_CONSTANTS_H */
diff --git a/backends/alsa/alsa-device.c b/backends/alsa/alsa-device.c
index 5acc6f5..f7f705e 100644
--- a/backends/alsa/alsa-device.c
+++ b/backends/alsa/alsa-device.c
@@ -15,7 +15,9 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <strings.h>
+#include <string.h>
+#include <libintl.h>
+
#include <glib.h>
#include <glib/gi18n.h>
#include <glib-object.h>
@@ -35,6 +37,18 @@
#define ALSA_DEVICE_ICON "audio-card"
+#define ALSA_STREAM_CONTROL_GET_SCORE(c) \
+ (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (c), \
+ "__matemixer_alsa_control_score")))
+
+#define ALSA_STREAM_CONTROL_SET_SCORE(c,score) \
+ (g_object_set_data (G_OBJECT (c), \
+ "__matemixer_alsa_control_score", \
+ GINT_TO_POINTER (score)))
+
+#define ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE(s) \
+ (ALSA_STREAM_CONTROL_GET_SCORE (alsa_stream_get_default_control (ALSA_STREAM (s))))
+
struct _AlsaDevicePrivate
{
snd_mixer_t *handle;
@@ -43,7 +57,8 @@ struct _AlsaDevicePrivate
GCond cond;
AlsaStream *input;
AlsaStream *output;
- GHashTable *switches;
+ GList *streams;
+ GList *switches;
gboolean events_pending;
};
@@ -61,64 +76,83 @@ static void alsa_device_finalize (GObject *object);
G_DEFINE_TYPE (AlsaDevice, alsa_device, MATE_MIXER_TYPE_DEVICE)
-static MateMixerSwitch *alsa_device_get_switch (MateMixerDevice *mmd,
- const gchar *name);
+static const GList * alsa_device_list_streams (MateMixerDevice *mmd);
+static const GList * alsa_device_list_switches (MateMixerDevice *mmd);
+
+static gboolean add_stream_input_control (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_control (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+
+static gboolean add_switch (AlsaDevice *device,
+ AlsaStream *stream,
+ snd_mixer_elem_t *el);
-static GList * alsa_device_list_streams (MateMixerDevice *mmd);
-static GList * alsa_device_list_switches (MateMixerDevice *mmd);
+static gboolean add_device_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_stream_input_control (AlsaDevice *device,
- snd_mixer_elem_t *el);
-static gboolean add_stream_output_control (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static gboolean add_stream_input_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_switch (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_switch (AlsaDevice *device,
- AlsaStream *stream,
- snd_mixer_elem_t *el);
+static gboolean add_stream_input_toggle (AlsaDevice *device,
+ snd_mixer_elem_t *el);
+static gboolean add_stream_output_toggle (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_device_switch (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void load_element (AlsaDevice *device,
+ snd_mixer_elem_t *el);
-static gboolean add_stream_input_switch (AlsaDevice *device,
- snd_mixer_elem_t *el);
-static gboolean add_stream_output_switch (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void load_elements_by_name (AlsaDevice *device,
+ const gchar *name);
-static gboolean add_stream_input_toggle (AlsaDevice *device,
- snd_mixer_elem_t *el);
-static gboolean add_stream_output_toggle (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void remove_elements_by_name (AlsaDevice *device,
+ const gchar *name);
-static void load_element (AlsaDevice *device,
- snd_mixer_elem_t *el);
+static void handle_poll (AlsaDevice *device);
-static void load_elements_by_name (AlsaDevice *device,
- const gchar *name);
+static gboolean handle_process_events (AlsaDevice *device);
-static void remove_elements_by_name (AlsaDevice *device,
- const gchar *name);
+static int handle_callback (snd_mixer_t *handle,
+ guint mask,
+ snd_mixer_elem_t *el);
+static int handle_element_callback (snd_mixer_elem_t *el,
+ guint mask);
-static void handle_poll (AlsaDevice *device);
+static void validate_default_controls (AlsaDevice *device);
-static gboolean handle_process_events (AlsaDevice *device);
+static AlsaStreamControl *get_best_stream_control (AlsaStream *stream);
-static int handle_callback (snd_mixer_t *handle,
- guint mask,
- snd_mixer_elem_t *el);
-static int handle_element_callback (snd_mixer_elem_t *el,
- guint mask);
+static gchar * get_element_name (snd_mixer_elem_t *el);
-static void close_device (AlsaDevice *device);
+static void get_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score);
+static void get_input_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score);
+static void get_output_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score);
-static gchar * get_element_name (snd_mixer_elem_t *el);
-static void get_control_info (snd_mixer_elem_t *el,
- gchar **name,
- gchar **label,
- MateMixerStreamControlRole *role);
+static void get_switch_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerSwitchRole *role);
-static void get_switch_info (snd_mixer_elem_t *el,
- gchar **name,
- gchar **label);
+static void close_mixer (AlsaDevice *device);
+
+static void free_stream_list (AlsaDevice *device);
+
+static gint compare_switch_name (gconstpointer a,
+ gconstpointer b);
static void
alsa_device_class_init (AlsaDeviceClass *klass)
@@ -131,14 +165,13 @@ alsa_device_class_init (AlsaDeviceClass *klass)
object_class->finalize = alsa_device_finalize;
device_class = MATE_MIXER_DEVICE_CLASS (klass);
- device_class->get_switch = alsa_device_get_switch;
device_class->list_streams = alsa_device_list_streams;
device_class->list_switches = alsa_device_list_switches;
signals[CLOSED] =
g_signal_new ("closed",
G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
+ G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (AlsaDeviceClass, closed),
NULL,
NULL,
@@ -157,11 +190,6 @@ alsa_device_init (AlsaDevice *device)
ALSA_TYPE_DEVICE,
AlsaDevicePrivate);
- device->priv->switches = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
device->priv->context = g_main_context_ref_thread_default ();
g_mutex_init (&device->priv->mutex);
@@ -178,7 +206,12 @@ alsa_device_dispose (GObject *object)
g_clear_object (&device->priv->input);
g_clear_object (&device->priv->output);
- g_hash_table_remove_all (device->priv->switches);
+ if (device->priv->switches != NULL) {
+ g_list_free_full (device->priv->switches, g_object_unref);
+ device->priv->switches = NULL;
+ }
+
+ free_stream_list (device);
G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object);
}
@@ -193,11 +226,9 @@ alsa_device_finalize (GObject *object)
g_mutex_clear (&device->priv->mutex);
g_cond_clear (&device->priv->cond);
- g_hash_table_unref (device->priv->switches);
g_main_context_unref (device->priv->context);
- if (device->priv->handle != NULL)
- snd_mixer_close (device->priv->handle);
+ close_mixer (device);
G_OBJECT_CLASS (alsa_device_parent_class)->dispose (object);
}
@@ -208,7 +239,7 @@ alsa_device_new (const gchar *name, const gchar *label)
AlsaDevice *device;
gchar *stream_name;
- g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
g_return_val_if_fail (label != NULL, NULL);
device = g_object_new (ALSA_TYPE_DEVICE,
@@ -223,13 +254,13 @@ alsa_device_new (const gchar *name, const gchar *label)
stream_name = g_strdup_printf ("alsa-input-%s", name);
device->priv->input = alsa_stream_new (stream_name,
MATE_MIXER_DEVICE (device),
- MATE_MIXER_STREAM_INPUT);
+ MATE_MIXER_DIRECTION_INPUT);
g_free (stream_name);
stream_name = g_strdup_printf ("alsa-output-%s", name);
device->priv->output = alsa_stream_new (stream_name,
MATE_MIXER_DEVICE (device),
- MATE_MIXER_STREAM_OUTPUT);
+ MATE_MIXER_DIRECTION_OUTPUT);
g_free (stream_name);
return device;
@@ -289,6 +320,72 @@ alsa_device_open (AlsaDevice *device)
return TRUE;
}
+gboolean
+alsa_device_is_open (AlsaDevice *device)
+{
+ g_return_val_if_fail (ALSA_IS_DEVICE (device), FALSE);
+
+ if (device->priv->handle != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+void
+alsa_device_close (AlsaDevice *device)
+{
+ GList *list;
+
+ g_return_if_fail (ALSA_IS_DEVICE (device));
+
+ if (device->priv->handle == NULL)
+ return;
+
+ /* Make each stream remove its controls and switches */
+ if (alsa_stream_has_controls_or_switches (device->priv->input) == TRUE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input));
+
+ alsa_stream_remove_all (device->priv->input);
+ free_stream_list (device);
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ name);
+ }
+
+ if (alsa_stream_has_controls_or_switches (device->priv->output) == TRUE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output));
+
+ alsa_stream_remove_all (device->priv->output);
+ free_stream_list (device);
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ name);
+ }
+
+ /* Remove device switches */
+ list = device->priv->switches;
+ while (list != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data);
+ GList *next = list->next;
+
+ device->priv->switches = g_list_delete_link (device->priv->switches, list);
+ g_signal_emit_by_name (G_OBJECT (device),
+ "switch-removed",
+ mate_mixer_switch_get_name (swtch));
+ g_object_unref (swtch);
+
+ list = next;
+ }
+
+ close_mixer (device);
+
+ g_signal_emit (G_OBJECT (device), signals[CLOSED], 0);
+}
+
void
alsa_device_load (AlsaDevice *device)
{
@@ -307,6 +404,9 @@ alsa_device_load (AlsaDevice *device)
el = snd_mixer_elem_next (el);
}
+ /* Assign proper default controls */
+ validate_default_controls (device);
+
/* Set callback for ALSA events */
snd_mixer_set_callback (device->priv->handle, handle_callback);
snd_mixer_set_callback_private (device->priv->handle, device);
@@ -332,7 +432,7 @@ alsa_device_get_input_stream (AlsaDevice *device)
/* Normally controlless streams should not exist, here we simulate the
* behaviour for the owning instance */
- if (alsa_stream_is_empty (device->priv->input) == FALSE)
+ if (alsa_stream_has_controls_or_switches (device->priv->input) == TRUE)
return device->priv->input;
return NULL;
@@ -345,7 +445,7 @@ alsa_device_get_output_stream (AlsaDevice *device)
/* Normally controlless streams should not exist, here we simulate the
* behaviour for the owning instance */
- if (alsa_stream_is_empty (device->priv->output) == FALSE)
+ if (alsa_stream_has_controls_or_switches (device->priv->output) == TRUE)
return device->priv->output;
return NULL;
@@ -360,44 +460,39 @@ add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element)
return FALSE;
if (stream != NULL) {
- gboolean empty = FALSE;
-
- if (alsa_stream_is_empty (stream) == TRUE) {
+ if (alsa_stream_has_controls_or_switches (stream) == FALSE) {
const gchar *name =
mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+ free_stream_list (device);
+
/* Pretend the stream has just been created now that we are adding
* the first control */
g_signal_emit_by_name (G_OBJECT (device),
"stream-added",
name);
- empty = TRUE;
}
if (ALSA_IS_STREAM_CONTROL (element)) {
+ /* Stream control */
alsa_stream_add_control (stream, ALSA_STREAM_CONTROL (element));
-
- /* If this is the first control, set it as the default one.
- * The controls often seem to come in the order of importance, but this is
- * driver specific, so we may later see if there is another control which
- * better matches the default. */
- if (empty == TRUE)
- alsa_stream_set_default_control (stream, ALSA_STREAM_CONTROL (element));
-
added = TRUE;
} else if (ALSA_IS_SWITCH (element)) {
/* Switch belonging to a stream */
alsa_stream_add_switch (stream, ALSA_SWITCH (element));
added = TRUE;
+ } else if (ALSA_IS_TOGGLE (element)) {
+ /* Toggle belonging to a stream */
+ alsa_stream_add_toggle (stream, ALSA_TOGGLE (element));
+ added = TRUE;
}
- } else if (ALSA_IS_SWITCH (element)) {
+ } else if (ALSA_IS_SWITCH (element) || ALSA_IS_TOGGLE (element)) {
/* Switch belonging to the device */
const gchar *name =
mate_mixer_switch_get_name (MATE_MIXER_SWITCH (element));
- g_hash_table_insert (device->priv->switches,
- g_strdup (name),
- g_object_ref (element));
+ device->priv->switches =
+ g_list_append (device->priv->switches, g_object_ref (element));
g_signal_emit_by_name (G_OBJECT (device),
"switch-added",
@@ -414,47 +509,32 @@ add_element (AlsaDevice *device, AlsaStream *stream, AlsaElement *element)
return added;
}
-static MateMixerSwitch *
-alsa_device_get_switch (MateMixerDevice *mmd, const gchar *name)
-{
- g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
- g_return_val_if_fail (name != NULL, NULL);
-
- return g_hash_table_lookup (ALSA_DEVICE (mmd)->priv->switches, name);
-}
-
-static GList *
+static const GList *
alsa_device_list_streams (MateMixerDevice *mmd)
{
AlsaDevice *device;
- GList *list = NULL;
g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
device = ALSA_DEVICE (mmd);
- if (device->priv->output != NULL)
- list = g_list_prepend (list, g_object_ref (device->priv->output));
- if (device->priv->input != NULL)
- list = g_list_prepend (list, g_object_ref (device->priv->input));
-
- return list;
+ if (device->priv->streams == NULL) {
+ if (device->priv->output != NULL)
+ device->priv->streams = g_list_prepend (device->priv->streams,
+ g_object_ref (device->priv->output));
+ if (device->priv->input != NULL)
+ device->priv->streams = g_list_prepend (device->priv->streams,
+ g_object_ref (device->priv->input));
+ }
+ return device->priv->streams;
}
-static GList *
+static const GList *
alsa_device_list_switches (MateMixerDevice *mmd)
{
- GList *list;
-
g_return_val_if_fail (ALSA_IS_DEVICE (mmd), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_DEVICE (mmd)->priv->switches);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return ALSA_DEVICE (mmd)->priv->switches;
}
static gboolean
@@ -463,25 +543,28 @@ add_stream_input_control (AlsaDevice *device, snd_mixer_elem_t *el)
AlsaStreamControl *control;
gchar *name;
gchar *label;
+ gboolean ret;
+ gint score;
MateMixerStreamControlRole role;
- get_control_info (el, &name, &label, &role);
+ get_input_control_info (el, &name, &label, &role, &score);
- g_debug ("Found device %s input control %s",
+ g_debug ("Reading device %s input control %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
- label);
+ name);
- control = alsa_stream_input_control_new (name, label, role);
+ control = alsa_stream_input_control_new (name, label, role, device->priv->input);
g_free (name);
g_free (label);
+ ALSA_STREAM_CONTROL_SET_SCORE (control, score);
+
alsa_element_set_snd_element (ALSA_ELEMENT (control), el);
- if (add_element (device, device->priv->input, ALSA_ELEMENT (control)) == FALSE) {
- g_object_unref (control);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->input, ALSA_ELEMENT (control));
+
+ g_object_unref (control);
+ return ret;
}
static gboolean
@@ -490,49 +573,51 @@ add_stream_output_control (AlsaDevice *device, snd_mixer_elem_t *el)
AlsaStreamControl *control;
gchar *label;
gchar *name;
+ gboolean ret;
+ gint score;
MateMixerStreamControlRole role;
- get_control_info (el, &name, &label, &role);
+ get_output_control_info (el, &name, &label, &role, &score);
- g_debug ("Found device %s output control %s",
+ g_debug ("Reading device %s output control %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
- label);
+ name);
- control = alsa_stream_output_control_new (name, label, role);
+ control = alsa_stream_output_control_new (name, label, role, device->priv->output);
g_free (name);
g_free (label);
+ ALSA_STREAM_CONTROL_SET_SCORE (control, score);
+
alsa_element_set_snd_element (ALSA_ELEMENT (control), el);
- if (add_element (device, device->priv->output, ALSA_ELEMENT (control)) == FALSE) {
- g_object_unref (control);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->output, ALSA_ELEMENT (control));
+
+ g_object_unref (control);
+ return ret;
}
static AlsaToggle *
create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type)
{
- AlsaToggle *toggle;
- AlsaSwitchOption *on;
- AlsaSwitchOption *off;
- gchar *name;
- gchar *label;
+ AlsaToggle *toggle;
+ AlsaSwitchOption *on;
+ AlsaSwitchOption *off;
+ gchar *name;
+ gchar *label;
+ MateMixerSwitchRole role;
on = alsa_switch_option_new ("On", _("On"), NULL, 1);
off = alsa_switch_option_new ("Off", _("Off"), NULL, 0);
- get_switch_info (el, &name, &label);
- toggle = alsa_toggle_new (name,
- label,
- type,
- on, off);
- g_free (name);
- g_free (label);
+ get_switch_info (el, &name, &label, &role);
+
+ toggle = alsa_toggle_new (name, label, role, type, on, off);
alsa_element_set_snd_element (ALSA_ELEMENT (toggle), el);
+ g_free (name);
+ g_free (label);
g_object_unref (on);
g_object_unref (off);
@@ -542,14 +627,15 @@ create_toggle (AlsaDevice *device, snd_mixer_elem_t *el, AlsaToggleType type)
static gboolean
add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el)
{
- AlsaElement *element = NULL;
- GList *options = NULL;
- gchar *name;
- gchar *label;
- gchar item[128];
- guint i;
- gint count;
- gint ret;
+ AlsaElement *element = NULL;
+ GList *options = NULL;
+ gchar *name;
+ gchar *label;
+ gchar item[128];
+ guint i;
+ gint count;
+ gboolean ret;
+ MateMixerSwitchRole role;
count = snd_mixer_selem_get_enum_items (el);
if G_UNLIKELY (count <= 0) {
@@ -560,30 +646,44 @@ add_switch (AlsaDevice *device, AlsaStream *stream, snd_mixer_elem_t *el)
}
for (i = 0; i < count; i++) {
- ret = snd_mixer_selem_get_enum_item_name (el, i,
- sizeof (item),
- item);
- if G_LIKELY (ret == 0)
- options = g_list_prepend (options,
- alsa_switch_option_new (item, item, NULL, i));
- else
+ gint ret = snd_mixer_selem_get_enum_item_name (el, i, sizeof (item), item);
+
+ if G_LIKELY (ret == 0) {
+ gint j;
+ AlsaSwitchOption *option = NULL;
+
+ for (j = 0; alsa_switch_options[j].name != NULL; j++)
+ if (strcmp (item, alsa_switch_options[j].name) == 0) {
+ option = alsa_switch_option_new (item,
+ gettext (alsa_switch_options[j].label),
+ alsa_switch_options[j].icon,
+ i);
+ break;
+ }
+
+ if (option == NULL)
+ option = alsa_switch_option_new (item, item, NULL, i);
+
+ options = g_list_prepend (options, option);
+ } else
g_warning ("Failed to read switch item name: %s", snd_strerror (ret));
}
- get_switch_info (el, &name, &label);
+ get_switch_info (el, &name, &label, &role);
/* Takes ownership of options */
- element = ALSA_ELEMENT (alsa_switch_new (name, label, g_list_reverse (options)));
+ element = ALSA_ELEMENT (alsa_switch_new (name, label,
+ role,
+ g_list_reverse (options)));
g_free (name);
g_free (label);
alsa_element_set_snd_element (element, el);
- if (add_element (device, stream, element) == FALSE) {
- g_object_unref (element);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, stream, element);
+
+ g_object_unref (element);
+ return ret;
}
static gboolean
@@ -623,6 +723,7 @@ static gboolean
add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
{
AlsaToggle *toggle;
+ gboolean ret;
g_debug ("Reading device %s input toggle %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
@@ -630,17 +731,17 @@ add_stream_input_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
toggle = create_toggle (device, el, ALSA_TOGGLE_CAPTURE);
- if (add_element (device, device->priv->input, ALSA_ELEMENT (toggle)) == FALSE) {
- g_object_unref (toggle);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->input, ALSA_ELEMENT (toggle));
+
+ g_object_unref (toggle);
+ return ret;
}
static gboolean
add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
{
AlsaToggle *toggle;
+ gboolean ret;
g_debug ("Reading device %s output toggle %s",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)),
@@ -648,11 +749,10 @@ add_stream_output_toggle (AlsaDevice *device, snd_mixer_elem_t *el)
toggle = create_toggle (device, el, ALSA_TOGGLE_PLAYBACK);
- if (add_element (device, device->priv->output, ALSA_ELEMENT (toggle)) == FALSE) {
- g_object_unref (toggle);
- return FALSE;
- }
- return TRUE;
+ ret = add_element (device, device->priv->output, ALSA_ELEMENT (toggle));
+
+ g_object_unref (toggle);
+ return ret;
}
static void
@@ -671,8 +771,7 @@ load_element (AlsaDevice *device, snd_mixer_elem_t *el)
penum = TRUE;
/* Enumerated controls which are not marked as capture or playback
- * are considered to be a part of the whole device, although sometimes
- * this is incorrectly reported by the driver */
+ * are considered to be a part of the whole device */
if (cenum == FALSE && penum == FALSE) {
add_device_switch (device, el);
}
@@ -707,25 +806,28 @@ load_element (AlsaDevice *device, snd_mixer_elem_t *el)
static void
load_elements_by_name (AlsaDevice *device, const gchar *name)
{
- AlsaElement *element;
+ GList *item;
alsa_stream_load_elements (device->priv->input, name);
alsa_stream_load_elements (device->priv->output, name);
- element = g_hash_table_lookup (device->priv->switches, name);
- if (element != NULL)
- alsa_element_load (element);
+ item = g_list_find_custom (device->priv->switches, name, compare_switch_name);
+ if (item != NULL)
+ alsa_element_load (ALSA_ELEMENT (item->data));
}
static void
remove_elements_by_name (AlsaDevice *device, const gchar *name)
{
+ GList *item;
+
if (alsa_stream_remove_elements (device->priv->input, name) == TRUE) {
/* Removing last stream element "removes" the stream */
- if (alsa_stream_is_empty (device->priv->input) == TRUE) {
+ if (alsa_stream_has_controls_or_switches (device->priv->input) == FALSE) {
const gchar *stream_name =
mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input));
+ free_stream_list (device);
g_signal_emit_by_name (G_OBJECT (device),
"stream-removed",
stream_name);
@@ -734,20 +836,28 @@ remove_elements_by_name (AlsaDevice *device, const gchar *name)
if (alsa_stream_remove_elements (device->priv->output, name) == TRUE) {
/* Removing last stream element "removes" the stream */
- if (alsa_stream_is_empty (device->priv->output) == TRUE) {
+ if (alsa_stream_has_controls_or_switches (device->priv->output) == FALSE) {
const gchar *stream_name =
mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output));
+ free_stream_list (device);
g_signal_emit_by_name (G_OBJECT (device),
"stream-removed",
stream_name);
}
}
- if (g_hash_table_remove (device->priv->switches, name) == TRUE)
+ item = g_list_find_custom (device->priv->switches, name, compare_switch_name);
+ if (item != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (item->data);
+
+ device->priv->switches = g_list_delete_link (device->priv->switches, item);
g_signal_emit_by_name (G_OBJECT (device),
"switch-removed",
- name);
+ mate_mixer_switch_get_name (swtch));
+
+ g_object_unref (swtch);
+ }
}
static void
@@ -806,7 +916,7 @@ handle_process_events (AlsaDevice *device)
if (device->priv->handle != NULL) {
gint ret = snd_mixer_handle_events (device->priv->handle);
if (ret < 0)
- close_device (device);
+ alsa_device_close (device);
}
device->priv->events_pending = FALSE;
@@ -826,7 +936,15 @@ handle_callback (snd_mixer_t *handle, guint mask, snd_mixer_elem_t *el)
if (mask & SND_CTL_EVENT_MASK_ADD) {
AlsaDevice *device = snd_mixer_get_callback_private (handle);
+ if (device->priv->handle == NULL) {
+ /* The mixer is already closed */
+ return 0;
+ }
+
load_element (device, el);
+
+ /* Revalidate default controls assignment */
+ validate_default_controls (device);
}
return 0;
}
@@ -838,6 +956,11 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask)
gchar *name;
device = snd_mixer_elem_get_callback_private (el);
+ if (device->priv->handle == NULL) {
+ /* The mixer is already closed */
+ return 0;
+ }
+
name = get_element_name (el);
if (mask == SND_CTL_EVENT_MASK_REMOVE) {
@@ -846,10 +969,16 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask)
snd_mixer_elem_set_callback (el, NULL);
remove_elements_by_name (device, name);
+
+ /* Revalidate default controls assignment */
+ validate_default_controls (device);
} else {
if (mask & SND_CTL_EVENT_MASK_INFO) {
remove_elements_by_name (device, name);
load_element (device, el);
+
+ /* Revalidate default controls assignment */
+ validate_default_controls (device);
}
if (mask & SND_CTL_EVENT_MASK_VALUE)
load_elements_by_name (device, name);
@@ -860,16 +989,86 @@ handle_element_callback (snd_mixer_elem_t *el, guint mask)
}
static void
-close_device (AlsaDevice *device)
+validate_default_controls (AlsaDevice *device)
{
- if (device->priv->handle != NULL) {
- snd_mixer_close (device->priv->handle);
- device->priv->handle = NULL;
+ AlsaStreamControl *best;
+ gint best_score;
+ gint current_score;
+
+ /*
+ * Select the most suitable default control. Don't try too hard here because
+ * our list of known elements is incomplete and most drivers seem to provide
+ * the list in a reasonable order with the best element at the start. Each
+ * element in our list has a value (or score) which is simply its position
+ * in the list. Better elements are on the top, so smaller value represents
+ * a better element.
+ *
+ * Two cases are handled here:
+ * 1) The current default control is in our list, but the list also includes
+ * a better element.
+ * 2) The current default control is not in our list, but the list includes
+ * an element which is reasonably good.
+ *
+ * In other cases just keep the first control as the default.
+ */
+ if (alsa_stream_has_controls (device->priv->input) == TRUE) {
+ best = get_best_stream_control (device->priv->input);
+
+ best_score = ALSA_STREAM_CONTROL_GET_SCORE (best);
+ current_score = ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE (device->priv->input);
+
+ /* See if the best element would make a good default one */
+ if (best_score > -1) {
+ g_debug ("Found usable default input element %s (score %d)",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (best)),
+ best_score);
+
+ if (current_score == -1 || best_score < current_score)
+ alsa_stream_set_default_control (device->priv->input, best);
+ }
}
- /* This signal tells the owner that the device has been closed voluntarily
- * from within the instance */
- g_signal_emit (G_OBJECT (device), signals[CLOSED], 0);
+ if (alsa_stream_has_controls (device->priv->output) == TRUE) {
+ best = get_best_stream_control (device->priv->output);
+
+ best_score = ALSA_STREAM_CONTROL_GET_SCORE (best);
+ current_score = ALSA_STREAM_DEFAULT_CONTROL_GET_SCORE (device->priv->output);
+
+ /* See if the best element would make a good default one */
+ if (best_score > -1) {
+ g_debug ("Found usable default output element %s (score %d)",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (best)),
+ best_score);
+
+ if (current_score == -1 || best_score < current_score)
+ alsa_stream_set_default_control (device->priv->output, best);
+ }
+ }
+}
+
+static AlsaStreamControl *
+get_best_stream_control (AlsaStream *stream)
+{
+ const GList *list;
+ AlsaStreamControl *best = NULL;
+ guint best_score = -1;
+
+ list = mate_mixer_stream_list_controls (MATE_MIXER_STREAM (stream));
+ while (list != NULL) {
+ AlsaStreamControl *current;
+ guint current_score;
+
+ current = ALSA_STREAM_CONTROL (list->data);
+ current_score = ALSA_STREAM_CONTROL_GET_SCORE (current);
+
+ if (best == NULL || (current_score != -1 &&
+ (best_score == -1 || current_score < best_score))) {
+ best = current;
+ best_score = current_score;
+ }
+ list = list->next;
+ }
+ return best;
}
static gchar *
@@ -884,7 +1083,8 @@ static void
get_control_info (snd_mixer_elem_t *el,
gchar **name,
gchar **label,
- MateMixerStreamControlRole *role)
+ MateMixerStreamControlRole *role,
+ gint *score)
{
MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
const gchar *n;
@@ -893,49 +1093,119 @@ get_control_info (snd_mixer_elem_t *el,
n = snd_mixer_selem_get_name (el);
- for (i = 0; alsa_controls[i].name != NULL; i++)
- if (strcmp (n, alsa_controls[i].name) == 0) {
- l = alsa_controls[i].label;
- r = alsa_controls[i].role;
- break;
- }
+ for (i = 0; alsa_controls[i].name != NULL; i++) {
+ if (strcmp (n, alsa_controls[i].name) != 0)
+ continue;
+
+ l = gettext (alsa_controls[i].label);
+ r = alsa_controls[i].role;
+ break;
+ }
*name = get_element_name (el);
- if (l != NULL)
+ if (l != NULL) {
*label = g_strdup (l);
- else
+ *score = i;
+ } else {
*label = g_strdup (n);
+ *score = -1;
+ }
*role = r;
}
static void
-get_switch_info (snd_mixer_elem_t *el,
- gchar **name,
- gchar **label)
+get_input_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score)
{
- // MateMixerStreamControlRole r = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
- const gchar *n;
- const gchar *l = NULL;
- // gint i;
+ get_control_info (el, name, label, role, score);
+
+ if (*score > -1 && alsa_controls[*score].use_default_input == FALSE)
+ *score = -1;
+}
+
+static void
+get_output_control_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerStreamControlRole *role,
+ gint *score)
+{
+ get_control_info (el, name, label, role, score);
+
+ if (*score > -1 && alsa_controls[*score].use_default_output == FALSE)
+ *score = -1;
+}
+
+static void
+get_switch_info (snd_mixer_elem_t *el,
+ gchar **name,
+ gchar **label,
+ MateMixerSwitchRole *role)
+{
+ MateMixerSwitchRole r = MATE_MIXER_SWITCH_ROLE_UNKNOWN;
+ const gchar *n;
+ const gchar *l = NULL;
+ gint i;
n = snd_mixer_selem_get_name (el);
- // TODO provide translated label and flags
+ for (i = 0; alsa_switches[i].name != NULL; i++) {
+ if (strcmp (n, alsa_switches[i].name) != 0)
+ continue;
+
+ l = gettext (alsa_switches[i].label);
+ r = alsa_switches[i].role;
+ break;
+ }
-/*
- for (i = 0; alsa_controls[i].name != NULL; i++)
- if (strcmp (n, alsa_controls[i].name) == 0) {
- l = alsa_controls[i].label;
- r = alsa_controls[i].role;
- break;
- }
-*/
*name = get_element_name (el);
if (l != NULL)
*label = g_strdup (l);
else
*label = g_strdup (n);
- // *role = r;
+ *role = r;
+}
+
+static void
+close_mixer (AlsaDevice *device)
+{
+ snd_mixer_t *handle;
+
+ if (device->priv->handle == NULL)
+ return;
+
+ /* Closing the mixer may fire up remove callbacks, prevent this by unsetting
+ * the handle before closing it and checking it in the callback.
+ * Ideally, we should unset callbacks from all the elements, but this seems
+ * to do the job. */
+ handle = device->priv->handle;
+
+ device->priv->handle = NULL;
+ snd_mixer_close (handle);
+}
+
+static void
+free_stream_list (AlsaDevice *device)
+{
+ /* This function is called each time the stream list changes */
+ if (device->priv->streams == NULL)
+ return;
+
+ g_list_free_full (device->priv->streams, g_object_unref);
+
+ device->priv->streams = NULL;
+}
+
+static gint
+compare_switch_name (gconstpointer a, gconstpointer b)
+{
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (a);
+ const gchar *name = (const gchar *) b;
+
+ return strcmp (mate_mixer_switch_get_name (swtch), name);
}
diff --git a/backends/alsa/alsa-device.h b/backends/alsa/alsa-device.h
index 3b3c970..9e908cf 100644
--- a/backends/alsa/alsa-device.h
+++ b/backends/alsa/alsa-device.h
@@ -20,6 +20,7 @@
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
#include "alsa-stream.h"
@@ -64,6 +65,9 @@ AlsaDevice *alsa_device_new (const gchar *name,
const gchar *label);
gboolean alsa_device_open (AlsaDevice *device);
+gboolean alsa_device_is_open (AlsaDevice *device);
+void alsa_device_close (AlsaDevice *device);
+
void alsa_device_load (AlsaDevice *device);
AlsaStream *alsa_device_get_input_stream (AlsaDevice *device);
diff --git a/backends/alsa/alsa-element.c b/backends/alsa/alsa-element.c
index f925064..d837965 100644
--- a/backends/alsa/alsa-element.c
+++ b/backends/alsa/alsa-element.c
@@ -51,3 +51,19 @@ alsa_element_load (AlsaElement *element)
return ALSA_ELEMENT_GET_INTERFACE (element)->load (element);
}
+
+void
+alsa_element_close (AlsaElement *element)
+{
+ AlsaElementInterface *iface;
+
+ g_return_if_fail (ALSA_IS_ELEMENT (element));
+
+ /* Close the element by unsetting the ALSA element and optionally calling
+ * a closing function */
+ alsa_element_set_snd_element (element, NULL);
+
+ iface = ALSA_ELEMENT_GET_INTERFACE (element);
+ if (iface->close != NULL)
+ iface->close (element);
+}
diff --git a/backends/alsa/alsa-element.h b/backends/alsa/alsa-element.h
index 01d30f1..1c30f68 100644
--- a/backends/alsa/alsa-element.h
+++ b/backends/alsa/alsa-element.h
@@ -46,6 +46,7 @@ struct _AlsaElementInterface
snd_mixer_elem_t *el);
gboolean (*load) (AlsaElement *element);
+ void (*close) (AlsaElement *element);
};
GType alsa_element_get_type (void) G_GNUC_CONST;
@@ -56,6 +57,8 @@ void alsa_element_set_snd_element (AlsaElement *element,
gboolean alsa_element_load (AlsaElement *element);
+void alsa_element_close (AlsaElement *element);
+
G_END_DECLS
#endif /* ALSA_ELEMENT_H */
diff --git a/backends/alsa/alsa-stream-control.c b/backends/alsa/alsa-stream-control.c
index bc7a937..97a0f8b 100644
--- a/backends/alsa/alsa-stream-control.c
+++ b/backends/alsa/alsa-stream-control.c
@@ -18,6 +18,7 @@
#include <glib.h>
#include <glib-object.h>
#include <alsa/asoundlib.h>
+
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
@@ -159,64 +160,67 @@ alsa_stream_control_set_data (AlsaStreamControl *control, AlsaControlData *data)
{
MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_NO_FLAGS;
MateMixerStreamControl *mmsc;
+ gboolean mute = FALSE;
g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
g_return_if_fail (data != NULL);
mmsc = MATE_MIXER_STREAM_CONTROL (control);
+ control->priv->data = *data;
+
g_object_freeze_notify (G_OBJECT (control));
if (data->channels > 0) {
if (data->switch_usable == TRUE) {
- flags |= MATE_MIXER_STREAM_CONTROL_HAS_MUTE;
+ /* If the mute switch is joined, all the channels get the same value,
+ * otherwise the element has per-channel mute, which we don't support.
+ * In that case, treat the control as unmuted if any channel is
+ * unmuted. */
+ if (data->channels == 1 || data->switch_joined == TRUE) {
+ mute = data->m[0];
+ } else {
+ gint i;
+ mute = TRUE;
+ for (i = 0; i < data->channels; i++)
+ if (data->m[i] == FALSE) {
+ mute = FALSE;
+ break;
+ }
+ }
+
+ flags |= MATE_MIXER_STREAM_CONTROL_MUTE_READABLE;
if (data->active == TRUE)
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_MUTE;
+ flags |= MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE;
}
- flags |= MATE_MIXER_STREAM_CONTROL_HAS_VOLUME;
+
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE;
if (data->active == TRUE)
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME;
- }
- if (data->max_decibel > -MATE_MIXER_INFINITY)
- flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
- control->priv->data = *data;
- control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels);
+ if (data->max_decibel > -MATE_MIXER_INFINITY)
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
- if (data->volume_joined == FALSE) {
- if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) &&
- MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask))
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+ control->priv->channel_mask = _mate_mixer_create_channel_mask (data->c, data->channels);
+
+ if (data->volume_joined == FALSE) {
+ if (MATE_MIXER_CHANNEL_MASK_HAS_LEFT (control->priv->channel_mask) &&
+ MATE_MIXER_CHANNEL_MASK_HAS_RIGHT (control->priv->channel_mask))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+
+ if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) &&
+ MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ }
- if (MATE_MIXER_CHANNEL_MASK_HAS_FRONT (control->priv->channel_mask) &&
- MATE_MIXER_CHANNEL_MASK_HAS_BACK (control->priv->channel_mask))
- flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ g_object_notify (G_OBJECT (control), "volume");
+ } else {
+ control->priv->channel_mask = 0;
}
+ _mate_mixer_stream_control_set_mute (mmsc, mute);
_mate_mixer_stream_control_set_flags (mmsc, flags);
- if (data->switch_usable == TRUE) {
- gboolean mute;
-
- /* If the mute switch is joined, all the channels get the same value,
- * otherwise the element has per-channel mute, which we don't support.
- * In that case, treat the control as unmuted if any channel is
- * unmuted. */
- if (data->channels == 1 || data->switch_joined == TRUE) {
- mute = data->m[0];
- } else {
- gint i;
- mute = TRUE;
- for (i = 0; i < data->channels; i++)
- if (data->m[i] == FALSE) {
- mute = FALSE;
- break;
- }
- }
- _mate_mixer_stream_control_set_mute (mmsc, mute);
- } else
- _mate_mixer_stream_control_set_mute (mmsc, FALSE);
-
if (flags & MATE_MIXER_STREAM_CONTROL_CAN_BALANCE)
_mate_mixer_stream_control_set_balance (mmsc, control_data_get_balance (data));
if (flags & MATE_MIXER_STREAM_CONTROL_CAN_FADE)
@@ -237,7 +241,6 @@ static void
alsa_stream_control_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
{
g_return_if_fail (ALSA_IS_STREAM_CONTROL (element));
- g_return_if_fail (el != NULL);
ALSA_STREAM_CONTROL (element)->priv->element = el;
}
@@ -288,6 +291,8 @@ alsa_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
for (i = 0; i < control->priv->data.channels; i++)
control->priv->data.m[i] = mute;
+
+ _mate_mixer_stream_control_set_mute (mmsc, mute);
}
return TRUE;
}
@@ -344,6 +349,8 @@ alsa_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)
for (i = 0; i < control->priv->data.channels; i++)
control->priv->data.v[i] = volume;
+ control->priv->data.volume = volume;
+
g_object_notify (G_OBJECT (control), "volume");
}
return TRUE;
@@ -364,7 +371,7 @@ alsa_stream_control_get_decibel (MateMixerStreamControl *mmsc)
volume = alsa_stream_control_get_volume (mmsc);
if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE)
- return FALSE;
+ return -MATE_MIXER_INFINITY;
return decibel;
}
@@ -428,7 +435,7 @@ alsa_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint chan
control = ALSA_STREAM_CONTROL (mmsc);
if (channel >= control->priv->data.channels)
- return FALSE;
+ return 0;
return control->priv->data.v[channel];
}
@@ -463,6 +470,7 @@ alsa_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint chan
if (klass->set_channel_volume (control, c, volume) == FALSE)
return FALSE;
+ // XXX recalc total volume
control->priv->data.v[channel] = volume;
g_object_notify (G_OBJECT (control), "volume");
@@ -483,13 +491,13 @@ alsa_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint cha
control = ALSA_STREAM_CONTROL (mmsc);
if (channel >= control->priv->data.channels)
- return FALSE;
+ return -MATE_MIXER_INFINITY;
klass = ALSA_STREAM_CONTROL_GET_CLASS (control);
volume = control->priv->data.v[channel];
if (klass->get_decibel_from_volume (control, volume, &decibel) == FALSE)
- return FALSE;
+ return -MATE_MIXER_INFINITY;
return decibel;
}
@@ -637,27 +645,27 @@ alsa_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade)
}
static guint
-alsa_stream_control_get_min_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_min_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.min;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.min;
}
static guint
-alsa_stream_control_get_max_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_max_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.max;
}
static guint
-alsa_stream_control_get_normal_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_normal_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.max;
}
static guint
-alsa_stream_control_get_base_volume (MateMixerStreamControl *msc)
+alsa_stream_control_get_base_volume (MateMixerStreamControl *mmsc)
{
- return ALSA_STREAM_CONTROL (msc)->priv->data.max;
+ return ALSA_STREAM_CONTROL (mmsc)->priv->data.max;
}
static void
diff --git a/backends/alsa/alsa-stream-control.h b/backends/alsa/alsa-stream-control.h
index f9ac6b6..acd02bd 100644
--- a/backends/alsa/alsa-stream-control.h
+++ b/backends/alsa/alsa-stream-control.h
@@ -41,9 +41,6 @@ typedef struct {
guint channels;
} AlsaControlData;
-extern const MateMixerChannelPosition alsa_channel_map_from[SND_MIXER_SCHN_LAST];
-extern const snd_mixer_selem_channel_id_t alsa_channel_map_to[MATE_MIXER_CHANNEL_MAX];
-
#define ALSA_TYPE_STREAM_CONTROL \
(alsa_stream_control_get_type ())
#define ALSA_STREAM_CONTROL(o) \
@@ -103,9 +100,6 @@ AlsaControlData * alsa_stream_control_get_data (AlsaStreamControl
void alsa_stream_control_set_data (AlsaStreamControl *control,
AlsaControlData *data);
-gboolean alsa_stream_control_set_role (AlsaStreamControl *control,
- MateMixerStreamControlRole role);
-
G_END_DECLS
#endif /* ALSA_STREAM_CONTROL_H */
diff --git a/backends/alsa/alsa-stream-input-control.c b/backends/alsa/alsa-stream-input-control.c
index 2ef0c42..2e3f46d 100644
--- a/backends/alsa/alsa-stream-input-control.c
+++ b/backends/alsa/alsa-stream-input-control.c
@@ -22,6 +22,7 @@
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
+#include "alsa-constants.h"
#include "alsa-element.h"
#include "alsa-stream-control.h"
#include "alsa-stream-input-control.h"
@@ -51,8 +52,7 @@ static gboolean alsa_stream_input_control_get_decibel_from_volume (AlsaStreamCon
guint volume,
gdouble *decibel);
-static void read_volume_data (snd_mixer_elem_t *el,
- AlsaControlData *data);
+static void read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data);
static void
alsa_stream_input_control_class_init (AlsaStreamInputControlClass *klass)
@@ -77,12 +77,14 @@ alsa_stream_input_control_init (AlsaStreamInputControl *control)
AlsaStreamControl *
alsa_stream_input_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role)
+ MateMixerStreamControlRole role,
+ AlsaStream *stream)
{
return g_object_new (ALSA_TYPE_STREAM_INPUT_CONTROL,
"name", name,
"label", label,
"role", role,
+ "stream", stream,
NULL);
}
@@ -98,7 +100,6 @@ alsa_stream_input_control_load (AlsaStreamControl *control)
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Expect that the element has a volume control */
if G_UNLIKELY (snd_mixer_selem_has_capture_volume (el) == 0 &&
snd_mixer_selem_has_common_volume (el) == 0) {
g_warn_if_reached ();
@@ -180,8 +181,7 @@ alsa_stream_input_control_set_channel_volume (AlsaStreamControl *contr
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Set the volume for a single channels, the volume may still be "joined" and
- * set all the channels by itself */
+ /* Set the volume for a single channel */
ret = snd_mixer_selem_set_capture_volume (el, channel, volume);
if (ret < 0) {
g_warning ("Failed to set channel volume: %s", snd_strerror (ret));
diff --git a/backends/alsa/alsa-stream-input-control.h b/backends/alsa/alsa-stream-input-control.h
index c427e3c..0ce885a 100644
--- a/backends/alsa/alsa-stream-input-control.h
+++ b/backends/alsa/alsa-stream-input-control.h
@@ -22,6 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include "alsa-stream.h"
#include "alsa-stream-control.h"
G_BEGIN_DECLS
@@ -57,7 +58,8 @@ GType alsa_stream_input_control_get_type (void) G_GNUC_CONST;
AlsaStreamControl *alsa_stream_input_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role);
+ MateMixerStreamControlRole role,
+ AlsaStream *stream);
G_END_DECLS
diff --git a/backends/alsa/alsa-stream-output-control.c b/backends/alsa/alsa-stream-output-control.c
index 5a3e6b3..1f4faf8 100644
--- a/backends/alsa/alsa-stream-output-control.c
+++ b/backends/alsa/alsa-stream-output-control.c
@@ -22,6 +22,7 @@
#include <libmatemixer/matemixer.h>
#include <libmatemixer/matemixer-private.h>
+#include "alsa-constants.h"
#include "alsa-element.h"
#include "alsa-stream-control.h"
#include "alsa-stream-output-control.h"
@@ -51,8 +52,7 @@ static gboolean alsa_stream_output_control_get_decibel_from_volume (AlsaStreamCo
guint volume,
gdouble *decibel);
-static void read_volume_data (snd_mixer_elem_t *el,
- AlsaControlData *data);
+static void read_volume_data (snd_mixer_elem_t *el, AlsaControlData *data);
static void
alsa_stream_output_control_class_init (AlsaStreamOutputControlClass *klass)
@@ -77,12 +77,14 @@ alsa_stream_output_control_init (AlsaStreamOutputControl *control)
AlsaStreamControl *
alsa_stream_output_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role)
+ MateMixerStreamControlRole role,
+ AlsaStream *stream)
{
return g_object_new (ALSA_TYPE_STREAM_OUTPUT_CONTROL,
"name", name,
"label", label,
"role", role,
+ "stream", stream,
NULL);
}
@@ -98,7 +100,6 @@ alsa_stream_output_control_load (AlsaStreamControl *control)
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Expect that the element has a volume control */
if G_UNLIKELY (snd_mixer_selem_has_playback_volume (el) == 0 &&
snd_mixer_selem_has_common_volume (el) == 0) {
g_warn_if_reached ();
@@ -180,8 +181,7 @@ alsa_stream_output_control_set_channel_volume (AlsaStreamControl *cont
if G_UNLIKELY (el == NULL)
return FALSE;
- /* Set the volume for a single channels, the volume may still be "joined" and
- * set all the channels by itself */
+ /* Set the volume for a single channel */
ret = snd_mixer_selem_set_playback_volume (el, channel, volume);
if (ret < 0) {
g_warning ("Failed to set channel volume: %s", snd_strerror (ret));
diff --git a/backends/alsa/alsa-stream-output-control.h b/backends/alsa/alsa-stream-output-control.h
index 845eaae..9a5b708 100644
--- a/backends/alsa/alsa-stream-output-control.h
+++ b/backends/alsa/alsa-stream-output-control.h
@@ -22,6 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include "alsa-stream.h"
#include "alsa-stream-control.h"
G_BEGIN_DECLS
@@ -57,7 +58,8 @@ GType alsa_stream_output_control_get_type (void) G_GNUC_CONST;
AlsaStreamControl *alsa_stream_output_control_new (const gchar *name,
const gchar *label,
- MateMixerStreamControlRole role);
+ MateMixerStreamControlRole role,
+ AlsaStream *stream);
G_END_DECLS
diff --git a/backends/alsa/alsa-stream.c b/backends/alsa/alsa-stream.c
index d2f68d4..bc9c1b5 100644
--- a/backends/alsa/alsa-stream.c
+++ b/backends/alsa/alsa-stream.c
@@ -18,6 +18,7 @@
#include <glib.h>
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include "alsa-element.h"
#include "alsa-stream.h"
@@ -26,27 +27,23 @@
struct _AlsaStreamPrivate
{
- GHashTable *switches;
- GHashTable *controls;
- MateMixerStreamControl *control;
+ GList *switches;
+ GList *controls;
};
static void alsa_stream_class_init (AlsaStreamClass *klass);
static void alsa_stream_init (AlsaStream *stream);
static void alsa_stream_dispose (GObject *object);
-static void alsa_stream_finalize (GObject *object);
G_DEFINE_TYPE (AlsaStream, alsa_stream, MATE_MIXER_TYPE_STREAM)
-static MateMixerStreamControl *alsa_stream_get_control (MateMixerStream *mms,
- const gchar *name);
-static MateMixerStreamControl *alsa_stream_get_default_control (MateMixerStream *mms);
-
-static MateMixerSwitch * alsa_stream_get_switch (MateMixerStream *mms,
- const gchar *name);
+static const GList *alsa_stream_list_controls (MateMixerStream *mms);
+static const GList *alsa_stream_list_switches (MateMixerStream *mms);
-static GList * alsa_stream_list_controls (MateMixerStream *mms);
-static GList * alsa_stream_list_switches (MateMixerStream *mms);
+static gint compare_control_name (gconstpointer a,
+ gconstpointer b);
+static gint compare_switch_name (gconstpointer a,
+ gconstpointer b);
static void
alsa_stream_class_init (AlsaStreamClass *klass)
@@ -55,15 +52,11 @@ alsa_stream_class_init (AlsaStreamClass *klass)
MateMixerStreamClass *stream_class;
object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = alsa_stream_dispose;
- object_class->finalize = alsa_stream_finalize;
+ object_class->dispose = alsa_stream_dispose;
stream_class = MATE_MIXER_STREAM_CLASS (klass);
- stream_class->get_control = alsa_stream_get_control;
- stream_class->get_default_control = alsa_stream_get_default_control;
- stream_class->get_switch = alsa_stream_get_switch;
- stream_class->list_controls = alsa_stream_list_controls;
- stream_class->list_switches = alsa_stream_list_switches;
+ stream_class->list_controls = alsa_stream_list_controls;
+ stream_class->list_switches = alsa_stream_list_switches;
g_type_class_add_private (object_class, sizeof (AlsaStreamPrivate));
}
@@ -74,16 +67,6 @@ alsa_stream_init (AlsaStream *stream)
stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
ALSA_TYPE_STREAM,
AlsaStreamPrivate);
-
- stream->priv->controls = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
- stream->priv->switches = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
}
static void
@@ -93,36 +76,30 @@ alsa_stream_dispose (GObject *object)
stream = ALSA_STREAM (object);
- g_hash_table_remove_all (stream->priv->controls);
- g_hash_table_remove_all (stream->priv->switches);
-
- g_clear_object (&stream->priv->control);
+ if (stream->priv->controls != NULL) {
+ g_list_free_full (stream->priv->controls, g_object_unref);
+ stream->priv->controls = NULL;
+ }
+ if (stream->priv->switches != NULL) {
+ g_list_free_full (stream->priv->switches, g_object_unref);
+ stream->priv->switches = NULL;
+ }
G_OBJECT_CLASS (alsa_stream_parent_class)->dispose (object);
}
-static void
-alsa_stream_finalize (GObject *object)
-{
- AlsaStream *stream;
-
- stream = ALSA_STREAM (object);
-
- g_hash_table_destroy (stream->priv->controls);
- g_hash_table_destroy (stream->priv->switches);
-
- G_OBJECT_CLASS (alsa_stream_parent_class)->finalize (object);
-}
-
AlsaStream *
-alsa_stream_new (const gchar *name,
- MateMixerDevice *device,
- MateMixerStreamFlags flags)
+alsa_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerDirection direction)
{
+ const gchar *label = mate_mixer_device_get_label (device);
+
return g_object_new (ALSA_TYPE_STREAM,
"name", name,
+ "label", label,
"device", device,
- "flags", flags,
+ "direction", direction,
NULL);
}
@@ -135,9 +112,16 @@ alsa_stream_add_control (AlsaStream *stream, AlsaStreamControl *control)
g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control));
- g_hash_table_insert (stream->priv->controls,
- g_strdup (name),
- g_object_ref (control));
+
+ stream->priv->controls =
+ g_list_append (stream->priv->controls, g_object_ref (control));
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-added",
+ name);
+
+ if (alsa_stream_has_default_control (stream) == FALSE)
+ alsa_stream_set_default_control (stream, control);
}
void
@@ -149,125 +133,250 @@ alsa_stream_add_switch (AlsaStream *stream, AlsaSwitch *swtch)
g_return_if_fail (ALSA_IS_SWITCH (swtch));
name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch));
- g_hash_table_insert (stream->priv->switches,
- g_strdup (name),
- g_object_ref (swtch));
+
+ stream->priv->switches =
+ g_list_append (stream->priv->switches, g_object_ref (swtch));
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-added",
+ name);
+}
+
+void
+alsa_stream_add_toggle (AlsaStream *stream, AlsaToggle *toggle)
+{
+ const gchar *name;
+
+ g_return_if_fail (ALSA_IS_STREAM (stream));
+ g_return_if_fail (ALSA_IS_TOGGLE (toggle));
+
+ name = mate_mixer_switch_get_name (MATE_MIXER_SWITCH (toggle));
+
+ /* Toggle is MateMixerSwitch, but not AlsaSwitch */
+ stream->priv->switches =
+ g_list_append (stream->priv->switches, g_object_ref (toggle));
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-added",
+ name);
+}
+
+gboolean
+alsa_stream_has_controls (AlsaStream *stream)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+
+ if (stream->priv->controls != NULL)
+ return TRUE;
+
+ return FALSE;
}
gboolean
-alsa_stream_is_empty (AlsaStream *stream)
+alsa_stream_has_switches (AlsaStream *stream)
{
g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
- if (g_hash_table_size (stream->priv->controls) > 0 ||
- g_hash_table_size (stream->priv->switches) > 0)
- return FALSE;
+ if (stream->priv->switches != NULL)
+ return TRUE;
- return TRUE;
+ return FALSE;
+}
+
+gboolean
+alsa_stream_has_controls_or_switches (AlsaStream *stream)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+
+ if (stream->priv->controls != NULL ||
+ stream->priv->switches != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean
+alsa_stream_has_default_control (AlsaStream *stream)
+{
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
+
+ if (mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)) != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+AlsaStreamControl *
+alsa_stream_get_default_control (AlsaStream *stream)
+{
+ MateMixerStreamControl *control;
+
+ g_return_val_if_fail (ALSA_IS_STREAM (stream), NULL);
+
+ control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream));
+ if (control != NULL)
+ return ALSA_STREAM_CONTROL (control);
+
+ return NULL;
}
void
alsa_stream_set_default_control (AlsaStream *stream, AlsaStreamControl *control)
{
g_return_if_fail (ALSA_IS_STREAM (stream));
- g_return_if_fail (ALSA_IS_STREAM_CONTROL (control));
-
- /* This function is only used internally so avoid validating that the control
- * belongs to this stream */
- if (stream->priv->control != NULL)
- g_object_unref (stream->priv->control);
+ g_return_if_fail (control == NULL || ALSA_IS_STREAM_CONTROL (control));
- if (control != NULL)
- stream->priv->control = MATE_MIXER_STREAM_CONTROL (g_object_ref (control));
+ if (control == NULL)
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream), NULL);
else
- stream->priv->control = NULL;
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream),
+ MATE_MIXER_STREAM_CONTROL (control));
}
void
alsa_stream_load_elements (AlsaStream *stream, const gchar *name)
{
- AlsaElement *element;
+ GList *item;
g_return_if_fail (ALSA_IS_STREAM (stream));
g_return_if_fail (name != NULL);
- element = g_hash_table_lookup (stream->priv->controls, name);
- if (element != NULL)
- alsa_element_load (element);
+ item = g_list_find_custom (stream->priv->controls, name, compare_control_name);
+ if (item != NULL)
+ alsa_element_load (ALSA_ELEMENT (item->data));
- element = g_hash_table_lookup (stream->priv->switches, name);
- if (element != NULL)
- alsa_element_load (element);
+ item = g_list_find_custom (stream->priv->switches, name, compare_switch_name);
+ if (item != NULL)
+ alsa_element_load (ALSA_ELEMENT (item->data));
}
gboolean
alsa_stream_remove_elements (AlsaStream *stream, const gchar *name)
{
+ GList *item;
gboolean removed = FALSE;
g_return_val_if_fail (ALSA_IS_STREAM (stream), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
- if (g_hash_table_remove (stream->priv->controls, name) == TRUE)
+ item = g_list_find_custom (stream->priv->controls, name, compare_control_name);
+ if (item != NULL) {
+ MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (item->data);
+
+ alsa_element_close (ALSA_ELEMENT (control));
+ stream->priv->controls = g_list_delete_link (stream->priv->controls, item);
+
+ /* Change the default control if we have just removed it */
+ if (control == mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream))) {
+ AlsaStreamControl *first = NULL;
+
+ if (stream->priv->controls != NULL)
+ first = ALSA_STREAM_CONTROL (stream->priv->controls->data);
+
+ alsa_stream_set_default_control (stream, first);
+ }
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-removed",
+ mate_mixer_stream_control_get_name (control));
+
+ g_object_unref (control);
removed = TRUE;
- if (g_hash_table_remove (stream->priv->switches, name) == TRUE)
+ }
+
+ item = g_list_find_custom (stream->priv->switches, name, compare_switch_name);
+ if (item != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (item->data);
+
+ alsa_element_close (ALSA_ELEMENT (swtch));
+
+ stream->priv->switches = g_list_delete_link (stream->priv->switches, item);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-removed",
+ mate_mixer_switch_get_name (swtch));
+
+ g_object_unref (swtch);
removed = TRUE;
+ }
return removed;
}
-static MateMixerStreamControl *
-alsa_stream_get_control (MateMixerStream *mms, const gchar *name)
+void
+alsa_stream_remove_all (AlsaStream *stream)
{
- g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+ GList *list;
- return g_hash_table_lookup (ALSA_STREAM (mms)->priv->controls, name);
-}
+ g_return_if_fail (ALSA_IS_STREAM (stream));
-static MateMixerStreamControl *
-alsa_stream_get_default_control (MateMixerStream *mms)
-{
- g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+ /* Remove all stream controls */
+ list = stream->priv->controls;
+ while (list != NULL) {
+ MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data);
+ GList *next = list->next;
+
+ alsa_element_close (ALSA_ELEMENT (control));
+
+ stream->priv->controls = g_list_delete_link (stream->priv->controls, list);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-removed",
+ mate_mixer_stream_control_get_name (control));
+
+ g_object_unref (control);
+ list = next;
+ }
+
+ /* Unset the default stream control */
+ alsa_stream_set_default_control (stream, NULL);
+
+ /* Remove all stream switches */
+ list = stream->priv->switches;
+ while (list != NULL) {
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (list->data);
+ GList *next = list->next;
+
+ alsa_element_close (ALSA_ELEMENT (swtch));
- return ALSA_STREAM (mms)->priv->control;
+ stream->priv->switches = g_list_delete_link (stream->priv->switches, list);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-removed",
+ mate_mixer_switch_get_name (swtch));
+
+ g_object_unref (swtch);
+ list = next;
+ }
}
-static MateMixerSwitch *
-alsa_stream_get_switch (MateMixerStream *mms, const gchar *name)
+static const GList *
+alsa_stream_list_controls (MateMixerStream *mms)
{
g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
- return g_hash_table_lookup (ALSA_STREAM (mms)->priv->switches, name);
+ return ALSA_STREAM (mms)->priv->controls;
}
-static GList *
-alsa_stream_list_controls (MateMixerStream *mms)
+static const GList *
+alsa_stream_list_switches (MateMixerStream *mms)
{
- GList *list;
-
g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->controls);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return ALSA_STREAM (mms)->priv->switches;
}
-static GList *
-alsa_stream_list_switches (MateMixerStream *mms)
+static gint
+compare_control_name (gconstpointer a, gconstpointer b)
{
- GList *list;
+ MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (a);
+ const gchar *name = (const gchar *) b;
- g_return_val_if_fail (ALSA_IS_STREAM (mms), NULL);
+ return strcmp (mate_mixer_stream_control_get_name (control), name);
+}
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (ALSA_STREAM (mms)->priv->switches);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
+static gint
+compare_switch_name (gconstpointer a, gconstpointer b)
+{
+ MateMixerSwitch *swtch = MATE_MIXER_SWITCH (a);
+ const gchar *name = (const gchar *) b;
- return list;
+ return strcmp (mate_mixer_switch_get_name (swtch), name);
}
diff --git a/backends/alsa/alsa-stream.h b/backends/alsa/alsa-stream.h
index f26a643..5aa3095 100644
--- a/backends/alsa/alsa-stream.h
+++ b/backends/alsa/alsa-stream.h
@@ -22,9 +22,9 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
-#include "alsa-element.h"
#include "alsa-stream-control.h"
#include "alsa-switch.h"
+#include "alsa-toggle.h"
G_BEGIN_DECLS
@@ -58,30 +58,35 @@ struct _AlsaStreamClass
MateMixerStreamClass parent_class;
};
-GType alsa_stream_get_type (void) G_GNUC_CONST;
+GType alsa_stream_get_type (void) G_GNUC_CONST;
-AlsaStream *alsa_stream_new (const gchar *name,
- MateMixerDevice *device,
- MateMixerStreamFlags flags);
+AlsaStream * alsa_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerDirection direction);
-void alsa_stream_add_control (AlsaStream *stream,
- AlsaStreamControl *control);
+void alsa_stream_add_control (AlsaStream *stream,
+ AlsaStreamControl *control);
+void alsa_stream_add_switch (AlsaStream *stream,
+ AlsaSwitch *swtch);
+void alsa_stream_add_toggle (AlsaStream *stream,
+ AlsaToggle *toggle);
-void alsa_stream_add_switch (AlsaStream *stream,
- AlsaSwitch *swtch);
+gboolean alsa_stream_has_controls (AlsaStream *stream);
+gboolean alsa_stream_has_switches (AlsaStream *stream);
+gboolean alsa_stream_has_controls_or_switches (AlsaStream *stream);
+gboolean alsa_stream_has_default_control (AlsaStream *stream);
-gboolean alsa_stream_is_empty (AlsaStream *stream);
+AlsaStreamControl *alsa_stream_get_default_control (AlsaStream *stream);
+void alsa_stream_set_default_control (AlsaStream *stream,
+ AlsaStreamControl *control);
-void alsa_stream_set_default_control (AlsaStream *stream,
- AlsaStreamControl *control);
+void alsa_stream_load_elements (AlsaStream *stream,
+ const gchar *name);
-void alsa_stream_load_elements (AlsaStream *stream,
- const gchar *name);
+gboolean alsa_stream_remove_elements (AlsaStream *stream,
+ const gchar *name);
-gboolean alsa_stream_remove_elements (AlsaStream *stream,
- const gchar *name);
-
-void alsa_stream_remove_all (AlsaStream *stream);
+void alsa_stream_remove_all (AlsaStream *stream);
G_END_DECLS
diff --git a/backends/alsa/alsa-switch-option.c b/backends/alsa/alsa-switch-option.c
index 2173113..1800df2 100644
--- a/backends/alsa/alsa-switch-option.c
+++ b/backends/alsa/alsa-switch-option.c
@@ -18,9 +18,7 @@
#include <glib.h>
#include <glib-object.h>
#include <alsa/asoundlib.h>
-
#include <libmatemixer/matemixer.h>
-#include <libmatemixer/matemixer-private.h>
#include "alsa-switch-option.h"
@@ -59,6 +57,7 @@ alsa_switch_option_new (const gchar *name,
option = g_object_new (ALSA_TYPE_SWITCH_OPTION,
"name", name,
"label", label,
+ "icon", icon,
NULL);
option->priv->id = id;
diff --git a/backends/alsa/alsa-switch-option.h b/backends/alsa/alsa-switch-option.h
index c2dda87..98d4f57 100644
--- a/backends/alsa/alsa-switch-option.h
+++ b/backends/alsa/alsa-switch-option.h
@@ -24,17 +24,17 @@
G_BEGIN_DECLS
-#define ALSA_TYPE_SWITCH_OPTION \
+#define ALSA_TYPE_SWITCH_OPTION \
(alsa_switch_option_get_type ())
-#define ALSA_SWITCH_OPTION(o) \
+#define ALSA_SWITCH_OPTION(o) \
(G_TYPE_CHECK_INSTANCE_CAST ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOption))
-#define ALSA_IS_SWITCH_OPTION(o) \
+#define ALSA_IS_SWITCH_OPTION(o) \
(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALSA_TYPE_SWITCH_OPTION))
-#define ALSA_SWITCH_OPTION_CLASS(k) \
+#define ALSA_SWITCH_OPTION_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass))
-#define ALSA_IS_SWITCH_OPTION_CLASS(k) \
+#define ALSA_IS_SWITCH_OPTION_CLASS(k) \
(G_TYPE_CHECK_CLASS_TYPE ((k), ALSA_TYPE_SWITCH_OPTION))
-#define ALSA_SWITCH_OPTION_GET_CLASS(o) \
+#define ALSA_SWITCH_OPTION_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), ALSA_TYPE_SWITCH_OPTION, AlsaSwitchOptionClass))
typedef struct _AlsaSwitchOption AlsaSwitchOption;
diff --git a/backends/alsa/alsa-switch.c b/backends/alsa/alsa-switch.c
index 15151ae..6a0f1f4 100644
--- a/backends/alsa/alsa-switch.c
+++ b/backends/alsa/alsa-switch.c
@@ -37,6 +37,7 @@ static void alsa_element_interface_init (AlsaElementInterface *iface);
static void alsa_switch_class_init (AlsaSwitchClass *klass);
static void alsa_switch_init (AlsaSwitch *swtch);
+static void alsa_switch_dispose (GObject *object);
G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch,
MATE_MIXER_TYPE_SWITCH,
@@ -46,7 +47,7 @@ G_DEFINE_TYPE_WITH_CODE (AlsaSwitch, alsa_switch,
static gboolean alsa_switch_set_active_option (MateMixerSwitch *mms,
MateMixerSwitchOption *mmso);
-static GList * alsa_switch_list_options (MateMixerSwitch *mms);
+static const GList * alsa_switch_list_options (MateMixerSwitch *mms);
static snd_mixer_elem_t * alsa_switch_get_snd_element (AlsaElement *element);
static void alsa_switch_set_snd_element (AlsaElement *element,
@@ -64,8 +65,12 @@ alsa_element_interface_init (AlsaElementInterface *iface)
static void
alsa_switch_class_init (AlsaSwitchClass *klass)
{
+ GObjectClass *object_class;
MateMixerSwitchClass *switch_class;
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = alsa_switch_dispose;
+
switch_class = MATE_MIXER_SWITCH_CLASS (klass);
switch_class->set_active_option = alsa_switch_set_active_option;
switch_class->list_options = alsa_switch_list_options;
@@ -74,6 +79,21 @@ alsa_switch_class_init (AlsaSwitchClass *klass)
}
static void
+alsa_switch_dispose (GObject *object)
+{
+ AlsaSwitch *swtch;
+
+ swtch = ALSA_SWITCH (object);
+
+ if (swtch->priv->options != NULL) {
+ g_list_free_full (swtch->priv->options, g_object_unref);
+ swtch->priv->options = NULL;
+ }
+
+ G_OBJECT_CLASS (alsa_switch_parent_class)->dispose (object);
+}
+
+static void
alsa_switch_init (AlsaSwitch *swtch)
{
swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch,
@@ -82,13 +102,17 @@ alsa_switch_init (AlsaSwitch *swtch)
}
AlsaSwitch *
-alsa_switch_new (const gchar *name, const gchar *label, GList *options)
+alsa_switch_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ GList *options)
{
AlsaSwitch *swtch;
swtch = g_object_new (ALSA_TYPE_SWITCH,
"name", name,
"label", label,
+ "role", role,
NULL);
/* Takes ownership of options */
@@ -109,6 +133,9 @@ alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso
swtch = ALSA_SWITCH (mms);
+ if G_UNLIKELY (swtch->priv->element == NULL)
+ return FALSE;
+
/* The channel mask is created when reading the active option the first
* time, so a successful load must be done before changing the option */
if G_UNLIKELY (swtch->priv->channel_mask == 0) {
@@ -136,12 +163,12 @@ alsa_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso
return set_item;
}
-static GList *
-alsa_switch_list_options (MateMixerSwitch *swtch)
+static const GList *
+alsa_switch_list_options (MateMixerSwitch *mms)
{
- g_return_val_if_fail (ALSA_IS_SWITCH (swtch), NULL);
+ g_return_val_if_fail (ALSA_IS_SWITCH (mms), NULL);
- return ALSA_SWITCH (swtch)->priv->options;
+ return ALSA_SWITCH (mms)->priv->options;
}
static snd_mixer_elem_t *
@@ -156,7 +183,6 @@ static void
alsa_switch_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
{
g_return_if_fail (ALSA_IS_SWITCH (element));
- g_return_if_fail (el != NULL);
ALSA_SWITCH (element)->priv->element = el;
}
@@ -170,8 +196,13 @@ alsa_switch_load (AlsaElement *element)
gint ret;
snd_mixer_selem_channel_id_t c;
+ g_return_val_if_fail (ALSA_IS_SWITCH (element), FALSE);
+
swtch = ALSA_SWITCH (element);
+ if G_UNLIKELY (swtch->priv->element == NULL)
+ return FALSE;
+
/* When reading the first time we try all the channels, otherwise only the
* ones which returned success before */
if (swtch->priv->channel_mask == 0) {
@@ -220,7 +251,7 @@ alsa_switch_load (AlsaElement *element)
}
g_warning ("Unknown active option of switch %s: %d",
- snd_mixer_selem_get_name (swtch->priv->element),
+ mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch)),
item);
return FALSE;
diff --git a/backends/alsa/alsa-switch.h b/backends/alsa/alsa-switch.h
index fdcfb87..b7f5931 100644
--- a/backends/alsa/alsa-switch.h
+++ b/backends/alsa/alsa-switch.h
@@ -56,9 +56,10 @@ struct _AlsaSwitchClass
GType alsa_switch_get_type (void) G_GNUC_CONST;
-AlsaSwitch *alsa_switch_new (const gchar *name,
- const gchar *label,
- GList *options);
+AlsaSwitch *alsa_switch_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ GList *options);
G_END_DECLS
diff --git a/backends/alsa/alsa-toggle.c b/backends/alsa/alsa-toggle.c
index efa3460..a7958c9 100644
--- a/backends/alsa/alsa-toggle.c
+++ b/backends/alsa/alsa-toggle.c
@@ -42,13 +42,13 @@ G_DEFINE_TYPE_WITH_CODE (AlsaToggle, alsa_toggle, MATE_MIXER_TYPE_TOGGLE,
G_IMPLEMENT_INTERFACE (ALSA_TYPE_ELEMENT,
alsa_element_interface_init))
-static gboolean alsa_toggle_set_active_option (MateMixerSwitch *mms,
- MateMixerSwitchOption *mmso);
+static gboolean alsa_toggle_set_active_option (MateMixerSwitch *mms,
+ MateMixerSwitchOption *mmso);
-static snd_mixer_elem_t * alsa_toggle_get_snd_element (AlsaElement *element);
-static void alsa_toggle_set_snd_element (AlsaElement *element,
- snd_mixer_elem_t *el);
-static gboolean alsa_toggle_load (AlsaElement *element);
+static snd_mixer_elem_t *alsa_toggle_get_snd_element (AlsaElement *element);
+static void alsa_toggle_set_snd_element (AlsaElement *element,
+ snd_mixer_elem_t *el);
+static gboolean alsa_toggle_load (AlsaElement *element);
static void
alsa_element_interface_init (AlsaElementInterface *iface)
@@ -78,17 +78,20 @@ alsa_toggle_init (AlsaToggle *toggle)
}
AlsaToggle *
-alsa_toggle_new (const gchar *name,
- const gchar *label,
- AlsaToggleType type,
- AlsaSwitchOption *on,
- AlsaSwitchOption *off)
+alsa_toggle_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ AlsaToggleType type,
+ AlsaSwitchOption *on,
+ AlsaSwitchOption *off)
{
AlsaToggle *toggle;
toggle = g_object_new (ALSA_TYPE_TOGGLE,
"name", name,
"label", label,
+ "flags", MATE_MIXER_SWITCH_TOGGLE,
+ "role", role,
"state-option-on", on,
"state-option-off", off,
NULL);
@@ -109,7 +112,12 @@ alsa_toggle_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso
toggle = ALSA_TOGGLE (mms);
- /* For toggles the 0/1 value is stored as the switch option id */
+ if G_UNLIKELY (toggle->priv->element == NULL)
+ return FALSE;
+
+ /* For toggles the 0/1 value is stored as the switch option id, there is not really
+ * a need to validate that the option belong to the switch, just make sure it
+ * contains the value 0 or 1 */
value = alsa_switch_option_get_id (ALSA_SWITCH_OPTION (mmso));
if G_UNLIKELY (value != 0 && value != 1) {
g_warn_if_reached ();
@@ -143,7 +151,6 @@ static void
alsa_toggle_set_snd_element (AlsaElement *element, snd_mixer_elem_t *el)
{
g_return_if_fail (ALSA_IS_TOGGLE (element));
- g_return_if_fail (el != NULL);
ALSA_TOGGLE (element)->priv->element = el;
}
@@ -158,6 +165,9 @@ alsa_toggle_load (AlsaElement *element)
toggle = ALSA_TOGGLE (element);
+ if G_UNLIKELY (toggle->priv->element == NULL)
+ return FALSE;
+
/* When reading the first time we try all the channels, otherwise only the
* ones which returned success before */
if (toggle->priv->channel_mask == 0) {
@@ -207,7 +217,6 @@ alsa_toggle_load (AlsaElement *element)
active = mate_mixer_toggle_get_state_option (MATE_MIXER_TOGGLE (toggle), FALSE);
_mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (toggle), active);
-
return TRUE;
}
diff --git a/backends/alsa/alsa-toggle.h b/backends/alsa/alsa-toggle.h
index d9c083b..1e1993c 100644
--- a/backends/alsa/alsa-toggle.h
+++ b/backends/alsa/alsa-toggle.h
@@ -63,11 +63,12 @@ struct _AlsaToggleClass
GType alsa_toggle_get_type (void) G_GNUC_CONST;
-AlsaToggle *alsa_toggle_new (const gchar *name,
- const gchar *label,
- AlsaToggleType type,
- AlsaSwitchOption *on,
- AlsaSwitchOption *off);
+AlsaToggle *alsa_toggle_new (const gchar *name,
+ const gchar *label,
+ MateMixerSwitchRole role,
+ AlsaToggleType type,
+ AlsaSwitchOption *on,
+ AlsaSwitchOption *off);
G_END_DECLS
diff --git a/backends/null/Makefile.am b/backends/null/Makefile.am
index 8ce28d1..08005e4 100644
--- a/backends/null/Makefile.am
+++ b/backends/null/Makefile.am
@@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer
backend_LTLIBRARIES = libmatemixer-null.la
AM_CPPFLAGS = \
+ -Wno-unknown-pragmas \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"libmatemixer-null\"
diff --git a/backends/null/null-backend.c b/backends/null/null-backend.c
index 49038b1..ee0ad2e 100644
--- a/backends/null/null-backend.c
+++ b/backends/null/null-backend.c
@@ -29,7 +29,10 @@ static void null_backend_class_init (NullBackendClass *klass);
static void null_backend_class_finalize (NullBackendClass *klass);
static void null_backend_init (NullBackend *null);
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
G_DEFINE_DYNAMIC_TYPE (NullBackend, null_backend, MATE_MIXER_TYPE_BACKEND)
+#pragma clang diagnostic pop
static gboolean null_backend_open (MateMixerBackend *backend);
diff --git a/backends/oss/Makefile.am b/backends/oss/Makefile.am
index 44caeb8..f535a37 100644
--- a/backends/oss/Makefile.am
+++ b/backends/oss/Makefile.am
@@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer
backend_LTLIBRARIES = libmatemixer-oss.la
AM_CPPFLAGS = \
+ -Wno-unknown-pragmas \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"libmatemixer-oss\"
@@ -19,7 +20,12 @@ libmatemixer_oss_la_SOURCES = \
oss-stream.c \
oss-stream.h \
oss-stream-control.c \
- oss-stream-control.h
+ oss-stream-control.h \
+ oss-switch.c \
+ oss-switch.h \
+ oss-switch-option.c \
+ oss-switch-option.h \
+ oss-types.h
libmatemixer_oss_la_LIBADD = \
$(GLIB_LIBS) \
diff --git a/backends/oss/oss-backend.c b/backends/oss/oss-backend.c
index 2b5eca7..23d265b 100644
--- a/backends/oss/oss-backend.c
+++ b/backends/oss/oss-backend.c
@@ -32,7 +32,7 @@
#include "oss-stream.h"
#define BACKEND_NAME "OSS"
-#define BACKEND_PRIORITY 9
+#define BACKEND_PRIORITY 10
#if !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__)
/* At least on systems based on FreeBSD we will need to read device names
@@ -47,7 +47,9 @@ struct _OssBackendPrivate
{
gchar *default_device;
GSource *timeout_source;
- GHashTable *devices;
+ GList *streams;
+ GList *devices;
+ GHashTable *devices_paths;
};
static void oss_backend_class_init (OssBackendClass *klass);
@@ -61,36 +63,49 @@ static void oss_backend_finalize (GObject *object);
G_DEFINE_DYNAMIC_TYPE (OssBackend, oss_backend, MATE_MIXER_TYPE_BACKEND)
#pragma clang diagnostic pop
-static gboolean oss_backend_open (MateMixerBackend *backend);
-static void oss_backend_close (MateMixerBackend *backend);
-static GList * oss_backend_list_devices (MateMixerBackend *backend);
-static GList * oss_backend_list_streams (MateMixerBackend *backend);
+static gboolean oss_backend_open (MateMixerBackend *backend);
+static void oss_backend_close (MateMixerBackend *backend);
+static const GList *oss_backend_list_devices (MateMixerBackend *backend);
+static const GList *oss_backend_list_streams (MateMixerBackend *backend);
-static gboolean read_devices (OssBackend *oss);
+static gboolean read_devices (OssBackend *oss);
-static gboolean read_device (OssBackend *oss,
- const gchar *path,
- gboolean *added);
+static gboolean read_device (OssBackend *oss,
+ const gchar *path,
+ gboolean *added);
-static gchar * read_device_label (OssBackend *oss,
- const gchar *path,
- gint fd);
+static gchar * read_device_label (OssBackend *oss,
+ const gchar *path,
+ gint fd);
-static gchar * read_device_label_sndstat (OssBackend *oss,
- const gchar *sndstat,
- const gchar *path,
- guint index) G_GNUC_UNUSED;
+static gchar * read_device_label_sndstat (OssBackend *oss,
+ const gchar *sndstat,
+ const gchar *path,
+ guint index) G_GNUC_UNUSED;
-static void add_device (OssBackend *oss,
- OssDevice *device);
-static void remove_device (OssBackend *oss,
- OssDevice *device);
+static void add_device (OssBackend *oss,
+ OssDevice *device);
+static void remove_device (OssBackend *oss,
+ OssDevice *device);
-static void remove_stream (OssBackend *oss,
- const gchar *name);
+static void remove_device_by_path (OssBackend *oss,
+ const gchar *path);
-static void select_default_input_stream (OssBackend *oss);
-static void select_default_output_stream (OssBackend *oss);
+static void remove_device_by_list_item (OssBackend *oss,
+ GList *item);
+
+static void remove_stream (OssBackend *oss,
+ const gchar *name);
+
+static void select_default_input_stream (OssBackend *oss);
+static void select_default_output_stream (OssBackend *oss);
+
+static void free_stream_list (OssBackend *oss);
+
+static gint compare_devices (gconstpointer a,
+ gconstpointer b);
+static gint compare_device_path (gconstpointer a,
+ gconstpointer b);
static MateMixerBackendInfo info;
@@ -142,10 +157,10 @@ oss_backend_init (OssBackend *oss)
OSS_TYPE_BACKEND,
OssBackendPrivate);
- oss->priv->devices = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
+ oss->priv->devices_paths = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
}
static void
@@ -170,7 +185,7 @@ oss_backend_finalize (GObject *object)
oss = OSS_BACKEND (object);
- g_hash_table_destroy (oss->priv->devices);
+ g_hash_table_unref (oss->priv->devices_paths);
G_OBJECT_CLASS (oss_backend_parent_class)->finalize (object);
}
@@ -212,63 +227,65 @@ oss_backend_close (MateMixerBackend *backend)
oss = OSS_BACKEND (backend);
g_source_destroy (oss->priv->timeout_source);
- g_hash_table_remove_all (oss->priv->devices);
- g_free (oss->priv->default_device);
- oss->priv->default_device = NULL;
+ if (oss->priv->devices != NULL) {
+ g_list_free_full (oss->priv->devices, g_object_unref);
+ oss->priv->devices = NULL;
+ }
+ if (oss->priv->default_device != NULL) {
+ g_free (oss->priv->default_device);
+ oss->priv->default_device = NULL;
+ }
+
+ free_stream_list (oss);
+
+ g_hash_table_remove_all (oss->priv->devices_paths);
_mate_mixer_backend_set_state (backend, MATE_MIXER_STATE_IDLE);
}
-static GList *
+static const GList *
oss_backend_list_devices (MateMixerBackend *backend)
{
- GList *list;
-
g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (OSS_BACKEND (backend)->priv->devices);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ return OSS_BACKEND (backend)->priv->devices;
}
-static GList *
+static const GList *
oss_backend_list_streams (MateMixerBackend *backend)
{
- OssBackend *oss;
- GHashTableIter iter;
- gpointer value;
- GList *list = NULL;
+ OssBackend *oss;
g_return_val_if_fail (OSS_IS_BACKEND (backend), NULL);
oss = OSS_BACKEND (backend);
- /* We don't keep a list or hash table of all streams here, instead walk
- * through the list of devices and create the list manually, each device
- * has at most one input and one output stream */
- g_hash_table_iter_init (&iter, oss->priv->devices);
+ if (oss->priv->streams == NULL) {
+ GList *list;
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- OssDevice *device = OSS_DEVICE (value);
- OssStream *stream;
+ /* Walk through the list of devices and create the stream list, each
+ * device has at most one input and one output stream */
+ list = oss->priv->devices;
- stream = oss_device_get_output_stream (device);
- if (stream != NULL)
- list = g_list_prepend (list, stream);
- stream = oss_device_get_input_stream (device);
- if (stream != NULL)
- list = g_list_prepend (list, stream);
- }
+ while (list != NULL) {
+ OssDevice *device = OSS_DEVICE (list->data);
+ OssStream *stream;
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return list;
+ stream = oss_device_get_input_stream (device);
+ if (stream != NULL) {
+ oss->priv->streams =
+ g_list_append (oss->priv->streams, g_object_ref (stream));
+ }
+ stream = oss_device_get_output_stream (device);
+ if (stream != NULL) {
+ oss->priv->streams =
+ g_list_append (oss->priv->streams, g_object_ref (stream));
+ }
+ list = list->next;
+ }
+ }
+ return oss->priv->streams;
}
static gboolean
@@ -278,8 +295,10 @@ read_devices (OssBackend *oss)
gboolean added = FALSE;
for (i = 0; i < OSS_MAX_DEVICES; i++) {
- gboolean added_current;
- gchar *path = g_strdup_printf ("/dev/mixer%i", i);
+ gchar *path;
+ gboolean added_current = FALSE;
+
+ path = g_strdup_printf ("/dev/mixer%i", i);
/* On recent FreeBSD both /dev/mixer and /dev/mixer0 point to the same
* mixer device, on NetBSD and OpenBSD /dev/mixer is a symlink to one
@@ -289,7 +308,7 @@ read_devices (OssBackend *oss)
if (read_device (oss, path, &added_current) == FALSE && i == 0)
read_device (oss, "/dev/mixer", &added_current);
- if (added_current)
+ if (added_current == TRUE)
added = TRUE;
g_free (path);
@@ -312,16 +331,12 @@ read_device (OssBackend *oss, const gchar *path, gboolean *added)
gchar *bname;
gchar *label;
- device = g_hash_table_lookup (oss->priv->devices, path);
- *added = FALSE;
-
fd = g_open (path, O_RDWR, 0);
if (fd == -1) {
if (errno != ENOENT && errno != ENXIO)
g_debug ("%s: %s", path, g_strerror (errno));
- if (device != NULL)
- remove_device (oss, device);
+ remove_device_by_path (oss, path);
return FALSE;
}
@@ -329,7 +344,7 @@ read_device (OssBackend *oss, const gchar *path, gboolean *added)
* still tested to be absolutely sure that the device is removed it case
* it has disappeared, but normally the device's polling facility should
* handle this by itself */
- if (device != NULL) {
+ if (g_hash_table_contains (oss->priv->devices_paths, path) == TRUE) {
close (fd);
return TRUE;
}
@@ -345,8 +360,9 @@ read_device (OssBackend *oss, const gchar *path, gboolean *added)
if ((*added = oss_device_open (device)) == TRUE)
add_device (oss, device);
+ else
+ g_object_unref (device);
- g_object_unref (device);
return *added;
}
@@ -445,44 +461,104 @@ read_device_label_sndstat (OssBackend *oss,
static void
add_device (OssBackend *oss, OssDevice *device)
{
- const gchar *name;
+ oss->priv->devices =
+ g_list_insert_sorted_with_data (oss->priv->devices,
+ device,
+ (GCompareDataFunc) compare_devices,
+ NULL);
- name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+ /* Keep track of added device paths */
+ g_hash_table_add (oss->priv->devices_paths,
+ g_strdup (oss_device_get_path (device)));
- g_hash_table_insert (oss->priv->devices,
- g_strdup (oss_device_get_path (device)),
- g_object_ref (device));
-
- // XXX make device emit it when closed
+ g_signal_connect_swapped (G_OBJECT (device),
+ "closed",
+ G_CALLBACK (remove_device),
+ oss);
g_signal_connect_swapped (G_OBJECT (device),
"stream-removed",
G_CALLBACK (remove_stream),
oss);
- g_signal_emit_by_name (G_OBJECT (oss), "device-added", name);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "closed",
+ G_CALLBACK (free_stream_list),
+ oss);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "stream-added",
+ G_CALLBACK (free_stream_list),
+ oss);
+ g_signal_connect_swapped (G_OBJECT (device),
+ "stream-removed",
+ G_CALLBACK (free_stream_list),
+ oss);
+ g_signal_emit_by_name (G_OBJECT (oss),
+ "device-added",
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+
+ /* Load the device elements after emitting device-added, because the load
+ * function will most likely emit stream-added on the device and backend */
oss_device_load (device);
}
static void
remove_device (OssBackend *oss, OssDevice *device)
{
- const gchar *name;
+ GList *item;
+
+ item = g_list_find (oss->priv->devices, device);
+ if (item != NULL)
+ remove_device_by_list_item (oss, item);
+}
+
+static void
+remove_device_by_path (OssBackend *oss, const gchar *path)
+{
+ GList *item;
+
+ item = g_list_find_custom (oss->priv->devices, path, compare_device_path);
+ if (item != NULL)
+ remove_device_by_list_item (oss, item);
+}
+
+static void
+remove_device_by_list_item (OssBackend *oss, GList *item)
+{
+ OssDevice *device;
const gchar *path;
- path = oss_device_get_path (device);
- name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+ device = OSS_DEVICE (item->data);
+
+ g_signal_handlers_disconnect_by_func (G_OBJECT (device),
+ G_CALLBACK (remove_device),
+ oss);
+
+ if (oss_device_is_open (device) == TRUE)
+ oss_device_close (device);
g_signal_handlers_disconnect_by_func (G_OBJECT (device),
G_CALLBACK (remove_stream),
oss);
- // XXX close the device and make it remove streams
- g_hash_table_remove (oss->priv->devices, path);
+ oss->priv->devices = g_list_delete_link (oss->priv->devices, item);
+
+ path = oss_device_get_path (device);
+ g_hash_table_remove (oss->priv->devices_paths, path);
+
+ if (g_strcmp0 (oss->priv->default_device, path) == 0) {
+ g_free (oss->priv->default_device);
+ oss->priv->default_device = NULL;
+ }
+
+ /* The list may and may not have been invalidate by device signals */
+ free_stream_list (oss);
g_signal_emit_by_name (G_OBJECT (oss),
"device-removed",
- name);
+ mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
+
+ g_object_unref (device);
}
static void
@@ -492,7 +568,6 @@ remove_stream (OssBackend *oss, const gchar *name)
stream = mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (oss));
- // XXX see if the change happens after stream is removed or before
if (stream != NULL && strcmp (mate_mixer_stream_get_name (stream), name) == 0)
select_default_input_stream (oss);
@@ -502,16 +577,31 @@ remove_stream (OssBackend *oss, const gchar *name)
select_default_output_stream (oss);
}
+static OssDevice *
+get_default_device (OssBackend *oss)
+{
+ GList *item;
+
+ if (oss->priv->default_device == NULL)
+ return NULL;
+
+ item = g_list_find_custom (oss->priv->devices,
+ oss->priv->default_device,
+ compare_device_path);
+ if G_LIKELY (item != NULL)
+ return OSS_DEVICE (item->data);
+
+ return NULL;
+}
+
static void
select_default_input_stream (OssBackend *oss)
{
- OssDevice *device = NULL;
+ OssDevice *device;
OssStream *stream;
- gint i;
+ GList *list;
- /* Always prefer stream in the "default" device */
- if (oss->priv->default_device != NULL)
- device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device);
+ device = get_default_device (oss);
if (device != NULL) {
stream = oss_device_get_input_stream (device);
if (stream != NULL) {
@@ -521,23 +611,17 @@ select_default_input_stream (OssBackend *oss)
}
}
- for (i = 0; i < OSS_MAX_DEVICES; i++) {
- gchar *path = g_strdup_printf ("/dev/mixer%i", i);
-
- device = g_hash_table_lookup (oss->priv->devices, path);
- if (device == NULL && i == 0)
- device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer");
+ list = oss->priv->devices;
+ while (list != NULL) {
+ device = OSS_DEVICE (list->data);
+ stream = oss_device_get_input_stream (device);
- if (device != NULL) {
- stream = oss_device_get_input_stream (device);
- if (stream != NULL) {
- _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss),
- MATE_MIXER_STREAM (stream));
- g_free (path);
- return;
- }
+ if (stream != NULL) {
+ _mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (oss),
+ MATE_MIXER_STREAM (stream));
+ return;
}
- g_free (path);
+ list = list->next;
}
/* In the worst case unset the default stream */
@@ -547,13 +631,11 @@ select_default_input_stream (OssBackend *oss)
static void
select_default_output_stream (OssBackend *oss)
{
- OssDevice *device = NULL;
+ OssDevice *device;
OssStream *stream;
- gint i;
+ GList *list;
- /* Always prefer stream in the "default" device */
- if (oss->priv->default_device != NULL)
- device = g_hash_table_lookup (oss->priv->devices, oss->priv->default_device);
+ device = get_default_device (oss);
if (device != NULL) {
stream = oss_device_get_output_stream (device);
if (stream != NULL) {
@@ -563,25 +645,48 @@ select_default_output_stream (OssBackend *oss)
}
}
- for (i = 0; i < OSS_MAX_DEVICES; i++) {
- gchar *path = g_strdup_printf ("/dev/mixer%i", i);
-
- device = g_hash_table_lookup (oss->priv->devices, path);
- if (device == NULL && i == 0)
- device = g_hash_table_lookup (oss->priv->devices, "/dev/mixer");
+ list = oss->priv->devices;
+ while (list != NULL) {
+ device = OSS_DEVICE (list->data);
+ stream = oss_device_get_output_stream (device);
- if (device != NULL) {
- stream = oss_device_get_output_stream (device);
- if (stream != NULL) {
- _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss),
- MATE_MIXER_STREAM (stream));
- g_free (path);
- return;
- }
+ if (stream != NULL) {
+ _mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss),
+ MATE_MIXER_STREAM (stream));
+ return;
}
- g_free (path);
+ list = list->next;
}
/* In the worst case unset the default stream */
_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (oss), NULL);
}
+
+static void
+free_stream_list (OssBackend *oss)
+{
+ if (oss->priv->streams == NULL)
+ return;
+
+ g_list_free_full (oss->priv->streams, g_object_unref);
+
+ oss->priv->streams = NULL;
+}
+
+static gint
+compare_devices (gconstpointer a, gconstpointer b)
+{
+ MateMixerDevice *d1 = MATE_MIXER_DEVICE (a);
+ MateMixerDevice *d2 = MATE_MIXER_DEVICE (b);
+
+ return strcmp (mate_mixer_device_get_name (d1), mate_mixer_device_get_name (d2));
+}
+
+static gint
+compare_device_path (gconstpointer a, gconstpointer b)
+{
+ OssDevice *device = OSS_DEVICE (a);
+ const gchar *path = (const gchar *) b;
+
+ return strcmp (oss_device_get_path (device), path);
+}
diff --git a/backends/oss/oss-backend.h b/backends/oss/oss-backend.h
index 325b61c..9617e79 100644
--- a/backends/oss/oss-backend.h
+++ b/backends/oss/oss-backend.h
@@ -20,9 +20,10 @@
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
-#include <libmatemixer/matemixer-backend.h>
-#include <libmatemixer/matemixer-backend-module.h>
+#include "oss-types.h"
#define OSS_TYPE_BACKEND \
(oss_backend_get_type ())
@@ -37,7 +38,6 @@
#define OSS_BACKEND_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_BACKEND, OssBackendClass))
-typedef struct _OssBackend OssBackend;
typedef struct _OssBackendClass OssBackendClass;
typedef struct _OssBackendPrivate OssBackendPrivate;
@@ -54,7 +54,7 @@ struct _OssBackendClass
MateMixerBackendClass parent_class;
};
-GType oss_backend_get_type (void) G_GNUC_CONST;
+GType oss_backend_get_type (void) G_GNUC_CONST;
/* Support function for dynamic loading of the backend module */
void backend_module_init (GTypeModule *module);
diff --git a/backends/oss/oss-device.c b/backends/oss/oss-device.c
index cf51705..44ed18f 100644
--- a/backends/oss/oss-device.c
+++ b/backends/oss/oss-device.c
@@ -16,7 +16,6 @@
*/
#include <errno.h>
-#include <unistd.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
@@ -29,87 +28,184 @@
#include "oss-device.h"
#include "oss-stream.h"
#include "oss-stream-control.h"
+#include "oss-switch-option.h"
+
+/*
+ * NOTES:
+ *
+ * OSS has a predefined list of channels (or "devices"), which historically used
+ * to be mapped to individual sound card pins. At this time, the channels are
+ * chosen somehow arbitrarily by drivers.
+ *
+ * Each of the channels may have a record switch, which toggles between playback
+ * and capture direction. OSS doesn't have mute switches and we can't really use
+ * the record switch as one. For this reason all channels are modelled as
+ * muteless stream controls and the record switch is modelled as a port switch.
+ *
+ * Also, we avoid modelling capturable channels as both input and output channels,
+ * because the ones which allow capture are normally capture-only channels
+ * (OSS just doesn't have the ability to make the distinction), so each channel in
+ * the list contains a flag of whether it can be used as a capture source, given
+ * that it's reported as capturable. Capturable channels are therefore modelled
+ * as input controls and this approach avoids for example putting PCM in an input
+ * stream (which makes no sense).
+ *
+ * OSS also has an indicator of whether the record switch is exclusive (only
+ * allows one capture source at a time), to simplify the lives of applications
+ * we always create a port switch and therefore assume the exclusivity is always
+ * true. Ideally, we should probably model a bunch of toggles, one for each channel
+ * with capture capability if they are known not to be exclusive.
+ */
#define OSS_DEVICE_ICON "audio-card"
-typedef enum
-{
+#define OSS_POLL_TIMEOUT_NORMAL 500
+#define OSS_POLL_TIMEOUT_RAPID 50
+#define OSS_POLL_TIMEOUT_RESTORE 3000
+
+typedef enum {
+ OSS_POLL_NORMAL,
+ OSS_POLL_RAPID
+} OssPollMode;
+
+typedef enum {
OSS_DEV_ANY,
OSS_DEV_INPUT,
OSS_DEV_OUTPUT
-} OssDevType;
+} OssDevChannelType;
-typedef struct
-{
+typedef struct {
gchar *name;
gchar *label;
MateMixerStreamControlRole role;
- OssDevType type;
-} OssDev;
-
-static const OssDev oss_devices[] = {
- { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_OUTPUT },
- { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, OSS_DEV_OUTPUT },
- { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, OSS_DEV_OUTPUT },
- { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT },
- { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT },
+ OssDevChannelType type;
+ gchar *icon;
+} OssDevChannel;
+
+/* Index of a channel in the array corresponds to the channel number passed to ioctl()s,
+ * device names are takes from soundcard.h */
+static const OssDevChannel oss_devices[] = {
+ { "vol", N_("Volume"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_OUTPUT, NULL },
+ { "bass", N_("Bass"), MATE_MIXER_STREAM_CONTROL_ROLE_BASS, OSS_DEV_OUTPUT, NULL },
+ { "treble", N_("Treble"), MATE_MIXER_STREAM_CONTROL_ROLE_TREBLE, OSS_DEV_OUTPUT, NULL },
+ { "synth", N_("Synth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT, NULL },
+ { "pcm", N_("PCM"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT, NULL },
/* OSS manual says this should be the beeper, but Linux OSS seems to assign it to
* regular volume control */
- { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, OSS_DEV_OUTPUT },
- { "line", N_("Line-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, OSS_DEV_INPUT },
+ { "speaker", N_("Speaker"), MATE_MIXER_STREAM_CONTROL_ROLE_SPEAKER, OSS_DEV_OUTPUT, NULL },
+ { "line", N_("Line In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL },
+ { "mic", N_("Microphone"), MATE_MIXER_STREAM_CONTROL_ROLE_MICROPHONE, OSS_DEV_INPUT, "audio-input-microphone" },
+ { "cd", N_("CD"), MATE_MIXER_STREAM_CONTROL_ROLE_CD, OSS_DEV_INPUT, NULL },
/* Recording monitor */
- { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT },
- { "pcm2", N_("PCM-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT },
+ { "mix", N_("Mixer"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT, NULL },
+ { "pcm2", N_("PCM 2"), MATE_MIXER_STREAM_CONTROL_ROLE_PCM, OSS_DEV_OUTPUT, NULL },
/* Recording level (master input) */
- { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_INPUT },
- { "igain", N_("In-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT },
- { "ogain", N_("Out-gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT },
- { "line1", N_("Line-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "line2", N_("Line-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "line3", N_("Line-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "dig1", N_("Digital-1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY },
- { "dig2", N_("Digital-2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY },
- { "dig3", N_("Digital-3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY },
- { "phin", N_("Phone-in"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "phout", N_("Phone-out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_OUTPUT },
- { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT },
- { "monitor", N_("Monitor"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT },
- { "depth", N_("3D-depth"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT },
- { "center", N_("3D-center"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT },
- { "midi", N_("MIDI"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT }
+ { "rec", N_("Record"), MATE_MIXER_STREAM_CONTROL_ROLE_MASTER, OSS_DEV_INPUT, NULL },
+ { "igain", N_("Input Gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_INPUT, NULL },
+ { "ogain", N_("Output Gain"), MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN, OSS_DEV_OUTPUT, NULL },
+ { "line1", N_("Line In 1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL },
+ { "line2", N_("Line In 2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL },
+ { "line3", N_("Line In 3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL },
+ /* These 3 can be attached to either digital input or output */
+ { "dig1", N_("Digital 1"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY, NULL },
+ { "dig2", N_("Digital 2"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY, NULL },
+ { "dig3", N_("Digital 3"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_ANY, NULL },
+ { "phin", N_("Phone In"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL },
+ { "phout", N_("Phone Out"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_OUTPUT, NULL },
+ { "video", N_("Video"), MATE_MIXER_STREAM_CONTROL_ROLE_VIDEO, OSS_DEV_INPUT, NULL },
+ { "radio", N_("Radio"), MATE_MIXER_STREAM_CONTROL_ROLE_PORT, OSS_DEV_INPUT, NULL },
+
+ /* soundcard.h on some systems include more channels, but different files provide
+ * different meanings for the remaining ones and the value is doubtful */
};
#define OSS_N_DEVICES MIN (G_N_ELEMENTS (oss_devices), SOUND_MIXER_NRDEVICES)
+/* Priorities for selecting default controls */
+static const gint oss_input_priority[] = {
+ 11, /* rec */
+ 12, /* igain */
+ 7, /* mic */
+ 6, /* line */
+ 14, /* line1 */
+ 15, /* line2 */
+ 16, /* line3 */
+ 17, /* dig1 */
+ 18, /* dig2 */
+ 19, /* dig3 */
+ 20, /* phin */
+ 8, /* cd */
+ 22, /* video */
+ 23, /* radio */
+ 3, /* synth */
+ 27 /* midi */
+};
+
+static const gint oss_output_priority[] = {
+ 0, /* vol */
+ 4, /* pcm */
+ 10, /* pcm2 */
+ 5, /* speaker */
+ 17, /* dig1 */
+ 18, /* dig2 */
+ 19, /* dig3 */
+ 25, /* depth */
+ 26, /* center */
+ 21, /* phone out */
+ 13, /* ogain */
+ 9, /* mix */
+ 24, /* monitor */
+ 1, /* bass */
+ 2 /* treble */
+};
+
struct _OssDevicePrivate
{
- gint fd;
- gchar *path;
- gint devmask;
- gint stereodevs;
- gint recmask;
- gint recsrc;
- OssStream *input;
- OssStream *output;
+ gint fd;
+ gchar *path;
+ gint devmask;
+ gint stereodevs;
+ gint recmask;
+ guint poll_tag;
+ guint poll_tag_restore;
+ guint poll_counter;
+ gboolean poll_use_counter;
+ OssPollMode poll_mode;
+ GList *streams;
+ OssStream *input;
+ OssStream *output;
+};
+
+enum {
+ CLOSED,
+ N_SIGNALS
};
-static void oss_device_class_init (OssDeviceClass *klass);
-static void oss_device_init (OssDevice *device);
-static void oss_device_dispose (GObject *object);
-static void oss_device_finalize (GObject *object);
+static guint signals[N_SIGNALS] = { 0, };
+
+static void oss_device_class_init (OssDeviceClass *klass);
+static void oss_device_init (OssDevice *device);
+static void oss_device_dispose (GObject *object);
+static void oss_device_finalize (GObject *object);
G_DEFINE_TYPE (OssDevice, oss_device, MATE_MIXER_TYPE_DEVICE)
-static GList * oss_device_list_streams (MateMixerDevice *device);
+static const GList *oss_device_list_streams (MateMixerDevice *mmd);
+
+static gboolean poll_mixer (OssDevice *device);
+static gboolean poll_mixer_restore (OssDevice *device);
+
+static void read_mixer_devices (OssDevice *device);
+static void read_mixer_switch (OssDevice *device);
-static gboolean read_mixer_devices (OssDevice *device);
+static guint create_poll_source (OssDevice *device,
+ OssPollMode mode);
+static guint create_poll_restore_source (OssDevice *device);
-static gboolean set_stream_default_control (OssStream *stream,
- OssStreamControl *control,
- gboolean force);
+static void free_stream_list (OssDevice *device);
+
+static gint compare_stream_control_devnum (gconstpointer a,
+ gconstpointer b);
static void
oss_device_class_init (OssDeviceClass *klass)
@@ -124,6 +220,18 @@ oss_device_class_init (OssDeviceClass *klass)
device_class = MATE_MIXER_DEVICE_CLASS (klass);
device_class->list_streams = oss_device_list_streams;
+ signals[CLOSED] =
+ g_signal_new ("closed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (OssDeviceClass, closed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0,
+ G_TYPE_NONE);
+
g_type_class_add_private (object_class, sizeof (OssDevicePrivate));
}
@@ -145,6 +253,8 @@ oss_device_dispose (GObject *object)
g_clear_object (&device->priv->input);
g_clear_object (&device->priv->output);
+ free_stream_list (device);
+
G_OBJECT_CLASS (oss_device_parent_class)->dispose (object);
}
@@ -153,13 +263,19 @@ oss_device_finalize (GObject *object)
{
OssDevice *device = OSS_DEVICE (object);
- close (device->priv->fd);
+ if (device->priv->fd != -1)
+ close (device->priv->fd);
+
+ g_free (device->priv->path);
G_OBJECT_CLASS (oss_device_parent_class)->finalize (object);
}
OssDevice *
-oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint fd)
+oss_device_new (const gchar *name,
+ const gchar *label,
+ const gchar *path,
+ gint fd)
{
OssDevice *device;
gchar *stream_name;
@@ -180,13 +296,13 @@ oss_device_new (const gchar *name, const gchar *label, const gchar *path, gint f
stream_name = g_strdup_printf ("oss-input-%s", name);
device->priv->input = oss_stream_new (stream_name,
MATE_MIXER_DEVICE (device),
- MATE_MIXER_STREAM_INPUT);
+ MATE_MIXER_DIRECTION_INPUT);
g_free (stream_name);
stream_name = g_strdup_printf ("oss-output-%s", name);
device->priv->output = oss_stream_new (stream_name,
MATE_MIXER_DEVICE (device),
- MATE_MIXER_STREAM_OUTPUT);
+ MATE_MIXER_DIRECTION_OUTPUT);
g_free (stream_name);
return device;
@@ -205,30 +321,19 @@ oss_device_open (OssDevice *device)
/* Read the essential information about the device, these values are not
* expected to change and will not be queried */
- ret = ioctl (device->priv->fd,
- MIXER_READ (SOUND_MIXER_DEVMASK),
+ ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_DEVMASK),
&device->priv->devmask);
- if (ret != 0)
+ if (ret == -1)
goto fail;
- ret = ioctl (device->priv->fd,
- MIXER_READ (SOUND_MIXER_STEREODEVS),
+ ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_STEREODEVS),
&device->priv->stereodevs);
- if (ret < 0)
+ if (ret == -1)
goto fail;
- ret = ioctl (device->priv->fd,
- MIXER_READ (SOUND_MIXER_RECMASK),
+ ret = ioctl (device->priv->fd, MIXER_READ (SOUND_MIXER_RECMASK),
&device->priv->recmask);
- if (ret < 0)
- goto fail;
-
- /* The recording source mask may change at any time, here we just read
- * the initial value */
- ret = ioctl (device->priv->fd,
- MIXER_READ (SOUND_MIXER_RECSRC),
- &device->priv->recsrc);
- if (ret < 0)
+ if (ret == -1)
goto fail;
/* NOTE: Linux also supports SOUND_MIXER_OUTSRC and SOUND_MIXER_OUTMASK which
@@ -246,41 +351,135 @@ fail:
}
gboolean
-oss_device_load (OssDevice *device)
+oss_device_is_open (OssDevice *device)
{
- MateMixerStreamControl *control;
-
g_return_val_if_fail (OSS_IS_DEVICE (device), FALSE);
- read_mixer_devices (device);
+ if (device->priv->fd != -1)
+ return TRUE;
+
+ return FALSE;
+}
+
+void
+oss_device_close (OssDevice *device)
+{
+ g_return_if_fail (OSS_IS_DEVICE (device));
- control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->input));
- if (control == NULL) {
- // XXX pick something
+ if (device->priv->fd == -1)
+ return;
+
+ /* Make each stream remove its controls and switch */
+ if (oss_stream_has_controls (device->priv->input) == TRUE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->input));
+
+ oss_stream_remove_all (device->priv->input);
+ free_stream_list (device);
+
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ name);
}
- if (control != NULL)
- g_debug ("Default input stream control is %s",
- mate_mixer_stream_control_get_label (control));
+ if (oss_stream_has_controls (device->priv->output) == TRUE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (device->priv->output));
+
+ oss_stream_remove_all (device->priv->output);
+ free_stream_list (device);
- control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (device->priv->output));
- if (control == NULL) {
- // XXX pick something
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ name);
}
- if (control != NULL)
- g_debug ("Default output stream control is %s",
- mate_mixer_stream_control_get_label (control));
+ if (device->priv->poll_tag != 0)
+ g_source_remove (device->priv->poll_tag);
- return TRUE;
+ if (device->priv->poll_tag_restore != 0)
+ g_source_remove (device->priv->poll_tag_restore);
+
+ close (device->priv->fd);
+ device->priv->fd = -1;
+
+ g_signal_emit (G_OBJECT (device), signals[CLOSED], 0);
}
-gint
-oss_device_get_fd (OssDevice *device)
+void
+oss_device_load (OssDevice *device)
{
- g_return_val_if_fail (OSS_IS_DEVICE (device), -1);
+ const GList *controls;
+ guint i;
+
+ g_return_if_fail (OSS_IS_DEVICE (device));
+
+ read_mixer_devices (device);
- return device->priv->fd;
+ /* Set default input control */
+ if (oss_stream_has_controls (device->priv->input) == TRUE) {
+ controls = mate_mixer_stream_list_controls (MATE_MIXER_STREAM (device->priv->input));
+
+ for (i = 0; i < G_N_ELEMENTS (oss_input_priority); i++) {
+ GList *item = g_list_find_custom ((GList *) controls,
+ GINT_TO_POINTER (oss_input_priority[i]),
+ compare_stream_control_devnum);
+ if (item == NULL)
+ continue;
+
+ oss_stream_set_default_control (device->priv->input,
+ OSS_STREAM_CONTROL (item->data));
+ break;
+ }
+ }
+
+ /* Set default output control */
+ if (oss_stream_has_controls (device->priv->output) == TRUE) {
+ controls = mate_mixer_stream_list_controls (MATE_MIXER_STREAM (device->priv->output));
+
+ for (i = 0; i < G_N_ELEMENTS (oss_output_priority); i++) {
+ GList *item = g_list_find_custom ((GList *) controls,
+ GINT_TO_POINTER (oss_output_priority[i]),
+ compare_stream_control_devnum);
+ if (item == NULL)
+ continue;
+
+ oss_stream_set_default_control (device->priv->output,
+ OSS_STREAM_CONTROL (item->data));
+ break;
+ }
+ }
+
+ if (device->priv->recmask != 0)
+ read_mixer_switch (device);
+
+ /* See if we can use the modify_counter field to optimize polling */
+#ifdef SOUND_MIXER_INFO
+ do {
+ struct mixer_info info;
+ gint ret;
+
+ ret = ioctl (device->priv->fd, SOUND_MIXER_INFO, &info);
+ if (ret == 0) {
+ device->priv->poll_counter = info.modify_counter;
+ device->priv->poll_use_counter = TRUE;
+ }
+ } while (0);
+#endif
+
+ /*
+ * Use a polling strategy inspired by KMix:
+ *
+ * Poll for changes with the OSS_POLL_TIMEOUT_NORMAL interval, when we
+ * encounter a change in modify_counter, decrease the interval to
+ * OSS_POLL_TIMEOUT_RAPID for a few seconds to allow for smoother
+ * adjustments, for example when user drags a slider.
+ *
+ * This way is not used on systems which don't support the modify_counter
+ * field, because there is no way to find out whether anything has
+ * changed and therefore when to start the rapid polling.
+ */
+ device->priv->poll_tag = create_poll_source (device, OSS_POLL_NORMAL);
}
const gchar *
@@ -307,98 +506,254 @@ oss_device_get_output_stream (OssDevice *device)
return device->priv->output;
}
-static GList *
+static const GList *
oss_device_list_streams (MateMixerDevice *mmd)
{
OssDevice *device;
- GList *list = NULL;
g_return_val_if_fail (OSS_IS_DEVICE (mmd), NULL);
device = OSS_DEVICE (mmd);
- if (device->priv->output != NULL)
- list = g_list_prepend (list, g_object_ref (device->priv->output));
- if (device->priv->input != NULL)
- list = g_list_prepend (list, g_object_ref (device->priv->input));
-
- return list;
+ if (device->priv->streams == NULL) {
+ if (device->priv->output != NULL)
+ device->priv->streams = g_list_prepend (device->priv->streams,
+ g_object_ref (device->priv->output));
+ if (device->priv->input != NULL)
+ device->priv->streams = g_list_prepend (device->priv->streams,
+ g_object_ref (device->priv->input));
+ }
+ return device->priv->streams;
}
#define OSS_MASK_HAS_DEVICE(mask,i) ((gboolean) (((mask) & (1 << (i))) > 0))
-static gboolean
+static void
read_mixer_devices (OssDevice *device)
{
- gint i;
+ OssStreamControl *control;
+ const gchar *name;
+ guint i;
+
+ name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
for (i = 0; i < OSS_N_DEVICES; i++) {
- OssStreamControl *control;
- gboolean input = FALSE;
+ OssStream *stream;
+ gboolean stereo;
/* Skip unavailable controls */
if (OSS_MASK_HAS_DEVICE (device->priv->devmask, i) == FALSE)
continue;
- if (oss_devices[i].type == OSS_DEV_ANY) {
- input = OSS_MASK_HAS_DEVICE (device->priv->recmask, i);
- }
- else if (oss_devices[i].type == OSS_DEV_INPUT) {
- input = TRUE;
- }
+ if (OSS_MASK_HAS_DEVICE (device->priv->recmask, i) == TRUE)
+ stream = device->priv->input;
+ else
+ stream = device->priv->output;
- control = oss_stream_control_new (oss_devices[i].name,
- oss_devices[i].label,
+ stereo = OSS_MASK_HAS_DEVICE (device->priv->stereodevs, i);
+ control = oss_stream_control_new (oss_devices[i].name, gettext (oss_devices[i].label),
oss_devices[i].role,
+ stream,
device->priv->fd,
i,
- OSS_MASK_HAS_DEVICE (device->priv->stereodevs, i));
-
- if (input == TRUE) {
- oss_stream_add_control (OSS_STREAM (device->priv->input), control);
-
- if (i == SOUND_MIXER_RECLEV || i == SOUND_MIXER_IGAIN) {
- if (i == SOUND_MIXER_RECLEV)
- set_stream_default_control (OSS_STREAM (device->priv->input),
- control,
- TRUE);
- else
- set_stream_default_control (OSS_STREAM (device->priv->input),
- control,
- FALSE);
- }
+ stereo);
+
+ if (oss_stream_has_controls (stream) == FALSE) {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+
+ free_stream_list (device);
+
+ /* Pretend the stream has just been created now that we are adding
+ * the first control */
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-added",
+ name);
+ }
+
+ g_debug ("Adding device %s control %s",
+ name,
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control)));
+
+ oss_stream_add_control (stream, control);
+ oss_stream_control_load (control);
+
+ g_object_unref (control);
+ }
+}
+
+static void
+read_mixer_switch (OssDevice *device)
+{
+ GList *options = NULL;
+ guint i;
+
+ for (i = 0; i < OSS_N_DEVICES; i++) {
+ OssSwitchOption *option;
+
+ if (OSS_MASK_HAS_DEVICE (device->priv->recmask, i) == FALSE)
+ continue;
+
+ option = oss_switch_option_new (oss_devices[i].name,
+ gettext (oss_devices[i].label),
+ oss_devices[i].icon,
+ i);
+ options = g_list_prepend (options, option);
+ }
+
+ if G_LIKELY (options != NULL)
+ oss_stream_set_switch_data (device->priv->input,
+ device->priv->fd,
+ options);
+}
+
+static gboolean
+poll_mixer (OssDevice *device)
+{
+ gboolean load = TRUE;
+
+ if G_UNLIKELY (device->priv->fd == -1)
+ return G_SOURCE_REMOVE;
+
+#ifdef SOUND_MIXER_INFO
+ if (device->priv->poll_use_counter == TRUE) {
+ gint ret;
+ struct mixer_info info;
+
+ /*
+ * The modify_counter field increases each time a change happens on
+ * the device.
+ *
+ * If this ioctl() works, we use the field to only poll the controls
+ * if a change actually occured and we can also adjust the poll interval.
+ *
+ * This works well at least on Linux, NetBSD and OpenBSD. This call is
+ * not supported on FreeBSD as of version 10.
+ *
+ * The call is also used to detect unplugged devices early, when not
+ * supported, the unplug is still caught in the backend.
+ */
+ ret = ioctl (device->priv->fd, SOUND_MIXER_INFO, &info);
+ if (ret == -1) {
+ if (errno == EINTR)
+ return G_SOURCE_CONTINUE;
+
+ oss_device_close (device);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (device->priv->poll_counter < info.modify_counter) {
+ device->priv->poll_counter = info.modify_counter;
} else {
- oss_stream_add_control (OSS_STREAM (device->priv->output), control);
-
- if (i == SOUND_MIXER_VOLUME || i == SOUND_MIXER_PCM) {
- if (i == SOUND_MIXER_VOLUME)
- set_stream_default_control (OSS_STREAM (device->priv->output),
- control,
- TRUE);
- else
- set_stream_default_control (OSS_STREAM (device->priv->output),
- control,
- FALSE);
- }
+ load = FALSE;
}
+ }
+#endif
+
+ if (load == TRUE) {
+ oss_stream_load (device->priv->input);
+ oss_stream_load (device->priv->output);
+
+ if (device->priv->poll_use_counter == TRUE &&
+ device->priv->poll_mode == OSS_POLL_NORMAL) {
+ /* Create a new rapid source */
+ device->priv->poll_tag = create_poll_source (device, OSS_POLL_RAPID);
- g_debug ("Added control %s",
- mate_mixer_stream_control_get_label (MATE_MIXER_STREAM_CONTROL (control)));
+ /* Also create another source to restore the poll interval to the
+ * original state */
+ device->priv->poll_tag_restore = create_poll_restore_source (device);
- oss_stream_control_update (control);
+ device->priv->poll_mode = OSS_POLL_RAPID;
+ return G_SOURCE_REMOVE;
+ }
}
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static gboolean
-set_stream_default_control (OssStream *stream, OssStreamControl *control, gboolean force)
+poll_mixer_restore (OssDevice *device)
{
- MateMixerStreamControl *current;
+ if G_LIKELY (device->priv->poll_mode == OSS_POLL_RAPID) {
+ /* Remove the current rapid source */
+ g_source_remove (device->priv->poll_tag);
- current = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream));
- if (current == NULL || force == TRUE) {
- oss_stream_set_default_control (stream, OSS_STREAM_CONTROL (control));
- return TRUE;
+ device->priv->poll_tag = create_poll_source (device, OSS_POLL_NORMAL);
+ device->priv->poll_mode = OSS_POLL_NORMAL;
}
- return FALSE;
+
+ /* Remove the tag for this function as it is only called once, the tag
+ * is only kept so we can remove it in the case the device is closed */
+ device->priv->poll_tag_restore = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static guint
+create_poll_source (OssDevice *device, OssPollMode mode)
+{
+ GSource *source;
+ guint timeout;
+ guint tag;
+
+ switch (mode) {
+ case OSS_POLL_NORMAL:
+ timeout = OSS_POLL_TIMEOUT_NORMAL;
+ break;
+ case OSS_POLL_RAPID:
+ timeout = OSS_POLL_TIMEOUT_RAPID;
+ break;
+ default:
+ g_warn_if_reached ();
+ return 0;
+ }
+
+ source = g_timeout_source_new (timeout);
+ g_source_set_callback (source,
+ (GSourceFunc) poll_mixer,
+ device,
+ NULL);
+
+ tag = g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+
+ return tag;
+}
+
+static guint
+create_poll_restore_source (OssDevice *device)
+{
+ GSource *source;
+ guint tag;
+
+ source = g_timeout_source_new (OSS_POLL_TIMEOUT_RESTORE);
+ g_source_set_callback (source,
+ (GSourceFunc) poll_mixer_restore,
+ device,
+ NULL);
+
+ tag = g_source_attach (source, g_main_context_get_thread_default ());
+ g_source_unref (source);
+
+ return tag;
+}
+
+static void
+free_stream_list (OssDevice *device)
+{
+ /* This function is called each time the stream list changes */
+ if (device->priv->streams == NULL)
+ return;
+
+ g_list_free_full (device->priv->streams, g_object_unref);
+
+ device->priv->streams = NULL;
+}
+
+static gint
+compare_stream_control_devnum (gconstpointer a, gconstpointer b)
+{
+ OssStreamControl *control = OSS_STREAM_CONTROL (a);
+ guint devnum = GPOINTER_TO_INT (b);
+
+ return !(oss_stream_control_get_devnum (control) == devnum);
}
diff --git a/backends/oss/oss-device.h b/backends/oss/oss-device.h
index 261a884..a723f41 100644
--- a/backends/oss/oss-device.h
+++ b/backends/oss/oss-device.h
@@ -22,7 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
-#include "oss-stream.h"
+#include "oss-types.h"
G_BEGIN_DECLS
@@ -39,7 +39,6 @@ G_BEGIN_DECLS
#define OSS_DEVICE_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_DEVICE, OssDeviceClass))
-typedef struct _OssDevice OssDevice;
typedef struct _OssDeviceClass OssDeviceClass;
typedef struct _OssDevicePrivate OssDevicePrivate;
@@ -54,6 +53,9 @@ struct _OssDevice
struct _OssDeviceClass
{
MateMixerDeviceClass parent;
+
+ /*< private >*/
+ void (*closed) (OssDevice *device);
};
GType oss_device_get_type (void) G_GNUC_CONST;
@@ -64,9 +66,11 @@ OssDevice * oss_device_new (const gchar *name,
gint fd);
gboolean oss_device_open (OssDevice *device);
-gboolean oss_device_load (OssDevice *device);
+gboolean oss_device_is_open (OssDevice *device);
+void oss_device_close (OssDevice *device);
+
+void oss_device_load (OssDevice *device);
-gint oss_device_get_fd (OssDevice *device);
const gchar *oss_device_get_path (OssDevice *device);
OssStream * oss_device_get_input_stream (OssDevice *device);
diff --git a/backends/oss/oss-stream-control.c b/backends/oss/oss-stream-control.c
index 5161528..0307fc7 100644
--- a/backends/oss/oss-stream-control.c
+++ b/backends/oss/oss-stream-control.c
@@ -26,27 +26,28 @@
#include "oss-common.h"
#include "oss-stream-control.h"
+#define OSS_VOLUME_JOIN(left,right) (((left) & 0xFF) | (((right) & 0xFF) << 8))
+
+#define OSS_VOLUME_JOIN_SAME(volume) (OSS_VOLUME_JOIN ((volume), (volume)))
+#define OSS_VOLUME_JOIN_ARRAY(volume) (OSS_VOLUME_JOIN ((volume[0]), (volume[1])))
+
+#define OSS_VOLUME_TAKE_LEFT(volume) ((volume) & 0xFF)
+#define OSS_VOLUME_TAKE_RIGHT(volume) (((volume) >> 8) & 0xFF)
+
struct _OssStreamControlPrivate
{
- gint fd;
- gint devnum;
- guint volume[2];
- gfloat balance;
- gboolean stereo;
- MateMixerStreamControlRole role;
- MateMixerStreamControlFlags flags;
+ gint fd;
+ gint devnum;
+ guint8 volume[2];
+ gboolean stereo;
};
static void oss_stream_control_class_init (OssStreamControlClass *klass);
-
static void oss_stream_control_init (OssStreamControl *control);
static void oss_stream_control_finalize (GObject *object);
G_DEFINE_TYPE (OssStreamControl, oss_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL)
-static gboolean oss_stream_control_set_mute (MateMixerStreamControl *mmsc,
- gboolean mute);
-
static guint oss_stream_control_get_num_channels (MateMixerStreamControl *mmsc);
static guint oss_stream_control_get_volume (MateMixerStreamControl *mmsc);
@@ -73,7 +74,9 @@ static guint oss_stream_control_get_max_volume (MateMix
static guint oss_stream_control_get_normal_volume (MateMixerStreamControl *mmsc);
static guint oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc);
-static gboolean write_volume (OssStreamControl *control,
+static void read_balance (OssStreamControl *control);
+
+static gboolean write_and_set_volume (OssStreamControl *control,
gint volume);
static void
@@ -86,14 +89,13 @@ oss_stream_control_class_init (OssStreamControlClass *klass)
object_class->finalize = oss_stream_control_finalize;
control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
- control_class->set_mute = oss_stream_control_set_mute;
control_class->get_num_channels = oss_stream_control_get_num_channels;
control_class->get_volume = oss_stream_control_get_volume;
control_class->set_volume = oss_stream_control_set_volume;
control_class->get_channel_volume = oss_stream_control_get_channel_volume;
control_class->set_channel_volume = oss_stream_control_set_channel_volume;
- control_class->get_channel_position = oss_stream_control_get_channel_position;
control_class->has_channel_position = oss_stream_control_has_channel_position;
+ control_class->get_channel_position = oss_stream_control_get_channel_position;
control_class->set_balance = oss_stream_control_set_balance;
control_class->get_min_volume = oss_stream_control_get_min_volume;
control_class->get_max_volume = oss_stream_control_get_max_volume;
@@ -118,7 +120,8 @@ oss_stream_control_finalize (GObject *object)
control = OSS_STREAM_CONTROL (object);
- close (control->priv->fd);
+ if (control->priv->fd != -1)
+ close (control->priv->fd);
G_OBJECT_CLASS (oss_stream_control_parent_class)->finalize (object);
}
@@ -127,6 +130,7 @@ OssStreamControl *
oss_stream_control_new (const gchar *name,
const gchar *label,
MateMixerStreamControlRole role,
+ OssStream *stream,
gint fd,
gint devnum,
gboolean stereo)
@@ -134,8 +138,11 @@ oss_stream_control_new (const gchar *name,
OssStreamControl *control;
MateMixerStreamControlFlags flags;
- flags = MATE_MIXER_STREAM_CONTROL_HAS_VOLUME |
- MATE_MIXER_STREAM_CONTROL_CAN_SET_VOLUME;
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+
+ flags = MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
if (stereo == TRUE)
flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
@@ -143,6 +150,8 @@ oss_stream_control_new (const gchar *name,
"name", name,
"label", label,
"flags", flags,
+ "role", role,
+ "stream", stream,
NULL);
control->priv->fd = fd;
@@ -151,52 +160,50 @@ oss_stream_control_new (const gchar *name,
return control;
}
-gboolean
-oss_stream_control_update (OssStreamControl *control)
+gint
+oss_stream_control_get_devnum (OssStreamControl *control)
{
- gint v;
- gint ret;
+ g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), 0);
- g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE);
+ return control->priv->devnum;
+}
- ret = ioctl (control->priv->fd, MIXER_READ (control->priv->devnum), &v);
- if (ret < 0) {
- g_warning ("Failed to read volume: %s", g_strerror (errno));
- return FALSE;
- }
+void
+oss_stream_control_load (OssStreamControl *control)
+{
+ gint v, ret;
- control->priv->volume[0] = v & 0xFF;
+ g_return_if_fail (OSS_IS_STREAM_CONTROL (control));
- if (control->priv->stereo == TRUE) {
- gfloat balance;
- guint left;
- guint right;
-
- control->priv->volume[1] = (v >> 8) & 0xFF;
-
- /* Calculate balance */
- left = control->priv->volume[0];
- right = control->priv->volume[1];
- if (left == right)
- balance = 0.0f;
- else if (left > right)
- balance = -1.0f + ((gfloat) right / (gfloat) left);
- else
- balance = +1.0f - ((gfloat) left / (gfloat) right);
+ if G_UNLIKELY (control->priv->fd == -1)
+ return;
+
+ ret = ioctl (control->priv->fd, MIXER_READ (control->priv->devnum), &v);
+ if (ret == -1)
+ return;
+
+ if (v != OSS_VOLUME_JOIN_ARRAY (control->priv->volume)) {
+ control->priv->volume[0] = OSS_VOLUME_TAKE_LEFT (v);
+ if (control->priv->stereo == TRUE)
+ control->priv->volume[1] = OSS_VOLUME_TAKE_RIGHT (v);
- _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control),
- balance);
+ g_object_notify (G_OBJECT (control), "volume");
}
- return TRUE;
+
+ if (control->priv->stereo == TRUE)
+ read_balance (control);
}
-static gboolean
-oss_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
+void
+oss_stream_control_close (OssStreamControl *control)
{
- g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE);
+ g_return_if_fail (OSS_IS_STREAM_CONTROL (control));
- // TODO
- return TRUE;
+ if (control->priv->fd == -1)
+ return;
+
+ close (control->priv->fd);
+ control->priv->fd = -1;
}
static guint
@@ -226,17 +233,15 @@ static gboolean
oss_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)
{
OssStreamControl *control;
- gint v;
g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE);
control = OSS_STREAM_CONTROL (mmsc);
- v = CLAMP (volume, 0, 100);
- if (control->priv->stereo == TRUE)
- v |= (volume & 0xFF) << 8;
+ if G_UNLIKELY (control->priv->fd == -1)
+ return FALSE;
- return write_volume (control, v);
+ return write_and_set_volume (control, OSS_VOLUME_JOIN_SAME (CLAMP (volume, 0, 100)));
}
static guint
@@ -269,27 +274,26 @@ oss_stream_control_set_channel_volume (MateMixerStreamControl *mmsc,
g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE);
control = OSS_STREAM_CONTROL (mmsc);
- volume = CLAMP (volume, 0, 100);
- if (control->priv->stereo == TRUE) {
- if (channel > 1)
- return FALSE;
+ if G_UNLIKELY (control->priv->fd == -1)
+ return FALSE;
+ if (channel > 1 || (control->priv->stereo == FALSE && channel > 0))
+ return FALSE;
- /* Stereo channel volume - left channel is in the lowest 8 bits and
+ volume = CLAMP (volume, 0, 100);
+
+ if (control->priv->stereo == TRUE) {
+ /* Stereo channel volume - left channel is in the lower 8 bits and
* right channel is in the higher 8 bits */
if (channel == 0)
- v = volume | (control->priv->volume[1] << 8);
+ v = OSS_VOLUME_JOIN (volume, control->priv->volume[1]);
else
- v = control->priv->volume[0] | (volume << 8);
+ v = OSS_VOLUME_JOIN (control->priv->volume[0], volume);
} else {
- if (channel > 0)
- return FALSE;
-
- /* Single channel volume - only lowest 8 bits are used */
+ /* Single channel volume - only lower 8 bits are used */
v = volume;
}
-
- return write_volume (control, v);
+ return write_and_set_volume (control, v);
}
static MateMixerChannelPosition
@@ -334,24 +338,24 @@ oss_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance)
{
OssStreamControl *control;
guint max;
- gint v;
+ gint volume[2];
g_return_val_if_fail (OSS_IS_STREAM_CONTROL (mmsc), FALSE);
control = OSS_STREAM_CONTROL (mmsc);
+ if G_UNLIKELY (control->priv->fd == -1)
+ return FALSE;
+
max = MAX (control->priv->volume[0], control->priv->volume[1]);
if (balance <= 0) {
- control->priv->volume[1] = (balance + 1.0f) * max;
- control->priv->volume[0] = max;
+ volume[1] = (balance + 1.0f) * max;
+ volume[0] = max;
} else {
- control->priv->volume[0] = (1.0f - balance) * max;
- control->priv->volume[1] = max;
+ volume[0] = (1.0f - balance) * max;
+ volume[1] = max;
}
-
- v = control->priv->volume[0] | (control->priv->volume[1] << 8);
-
- return write_volume (control, v);
+ return write_and_set_volume (control, OSS_VOLUME_JOIN_ARRAY (volume));
}
static guint
@@ -378,15 +382,45 @@ oss_stream_control_get_base_volume (MateMixerStreamControl *mmsc)
return 100;
}
+static void
+read_balance (OssStreamControl *control)
+{
+ gfloat balance;
+ guint left = control->priv->volume[0];
+ guint right = control->priv->volume[1];
+
+ if (left == right)
+ balance = 0.0f;
+ else if (left > right)
+ balance = -1.0f + ((gfloat) right / (gfloat) left);
+ else
+ balance = +1.0f - ((gfloat) left / (gfloat) right);
+
+ _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control),
+ balance);
+}
+
static gboolean
-write_volume (OssStreamControl *control, gint volume)
+write_and_set_volume (OssStreamControl *control, gint volume)
{
gint ret;
+ /* Nothing to do? */
+ if (volume == OSS_VOLUME_JOIN_ARRAY (control->priv->volume))
+ return TRUE;
+
ret = ioctl (control->priv->fd, MIXER_WRITE (control->priv->devnum), &volume);
- if (ret < 0) {
- g_warning ("Failed to set volume: %s", g_strerror (errno));
+ if (ret == -1)
return FALSE;
- }
+
+ oss_stream_control_load (control);
+ return TRUE;
+
+ /* The ioctl may make adjustments to the passed volume, so make sure we have
+ * the correct value saved */
+ control->priv->volume[0] = OSS_VOLUME_TAKE_LEFT (volume);
+ control->priv->volume[1] = OSS_VOLUME_TAKE_RIGHT (volume);
+
+ g_object_notify (G_OBJECT (control), "volume");
return TRUE;
}
diff --git a/backends/oss/oss-stream-control.h b/backends/oss/oss-stream-control.h
index c839faf..1957088 100644
--- a/backends/oss/oss-stream-control.h
+++ b/backends/oss/oss-stream-control.h
@@ -22,6 +22,8 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include "oss-types.h"
+
G_BEGIN_DECLS
#define OSS_TYPE_STREAM_CONTROL \
@@ -37,7 +39,6 @@ G_BEGIN_DECLS
#define OSS_STREAM_CONTROL_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_STREAM_CONTROL, OssStreamControlClass))
-typedef struct _OssStreamControl OssStreamControl;
typedef struct _OssStreamControlClass OssStreamControlClass;
typedef struct _OssStreamControlPrivate OssStreamControlPrivate;
@@ -54,16 +55,20 @@ struct _OssStreamControlClass
MateMixerStreamControlClass parent;
};
-GType oss_stream_control_get_type (void) G_GNUC_CONST;
+GType oss_stream_control_get_type (void) G_GNUC_CONST;
+
+OssStreamControl *oss_stream_control_new (const gchar *name,
+ const gchar *label,
+ MateMixerStreamControlRole role,
+ OssStream *stream,
+ gint fd,
+ gint devnum,
+ gboolean stereo);
-OssStreamControl *oss_stream_control_new (const gchar *name,
- const gchar *label,
- MateMixerStreamControlRole role,
- gint fd,
- gint devnum,
- gboolean stereo);
+gint oss_stream_control_get_devnum (OssStreamControl *control);
-gboolean oss_stream_control_update (OssStreamControl *control);
+void oss_stream_control_load (OssStreamControl *control);
+void oss_stream_control_close (OssStreamControl *control);
G_END_DECLS
diff --git a/backends/oss/oss-stream.c b/backends/oss/oss-stream.c
index 5f0c629..227c88b 100644
--- a/backends/oss/oss-stream.c
+++ b/backends/oss/oss-stream.c
@@ -16,32 +16,32 @@
*/
#include <glib.h>
+#include <glib/gi18n.h>
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
-#include "oss-device.h"
#include "oss-stream.h"
#include "oss-stream-control.h"
+#include "oss-switch.h"
+
+#define OSS_STREAM_SWITCH_NAME "port"
struct _OssStreamPrivate
{
- GHashTable *controls;
- OssStreamControl *control;
+ OssSwitch *swtch;
+ GList *switches;
+ GList *controls;
};
-static void oss_stream_class_init (OssStreamClass *klass);
-
-static void oss_stream_init (OssStream *ostream);
-static void oss_stream_dispose (GObject *object);
-static void oss_stream_finalize (GObject *object);
+static void oss_stream_class_init (OssStreamClass *klass);
+static void oss_stream_init (OssStream *stream);
+static void oss_stream_dispose (GObject *object);
G_DEFINE_TYPE (OssStream, oss_stream, MATE_MIXER_TYPE_STREAM)
-static MateMixerStreamControl *oss_stream_get_control (MateMixerStream *stream,
- const gchar *name);
-static MateMixerStreamControl *oss_stream_get_default_control (MateMixerStream *stream);
-
-static GList * oss_stream_list_controls (MateMixerStream *stream);
+static const GList *oss_stream_list_controls (MateMixerStream *mms);
+static const GList *oss_stream_list_switches (MateMixerStream *mms);
static void
oss_stream_class_init (OssStreamClass *klass)
@@ -50,13 +50,11 @@ oss_stream_class_init (OssStreamClass *klass)
MateMixerStreamClass *stream_class;
object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = oss_stream_dispose;
- object_class->finalize = oss_stream_finalize;
+ object_class->dispose = oss_stream_dispose;
stream_class = MATE_MIXER_STREAM_CLASS (klass);
- stream_class->get_control = oss_stream_get_control;
- stream_class->get_default_control = oss_stream_get_default_control;
- stream_class->list_controls = oss_stream_list_controls;
+ stream_class->list_controls = oss_stream_list_controls;
+ stream_class->list_switches = oss_stream_list_switches;
g_type_class_add_private (object_class, sizeof (OssStreamPrivate));
}
@@ -67,11 +65,6 @@ oss_stream_init (OssStream *stream)
stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
OSS_TYPE_STREAM,
OssStreamPrivate);
-
- stream->priv->controls = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
}
static void
@@ -81,103 +74,201 @@ oss_stream_dispose (GObject *object)
stream = OSS_STREAM (object);
- g_clear_object (&stream->priv->control);
- g_hash_table_remove_all (stream->priv->controls);
+ if (stream->priv->controls != NULL) {
+ g_list_free_full (stream->priv->controls, g_object_unref);
+ stream->priv->controls = NULL;
+ }
+ if (stream->priv->switches != NULL) {
+ g_list_free_full (stream->priv->switches, g_object_unref);
+ stream->priv->switches = NULL;
+ }
+
+ g_clear_object (&stream->priv->swtch);
G_OBJECT_CLASS (oss_stream_parent_class)->dispose (object);
}
-static void
-oss_stream_finalize (GObject *object)
+OssStream *
+oss_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerDirection direction)
{
- OssStream *stream;
+ const gchar *label = mate_mixer_device_get_label (device);
+
+ return g_object_new (OSS_TYPE_STREAM,
+ "name", name,
+ "label", label,
+ "device", device,
+ "direction", direction,
+ NULL);
+}
- stream = OSS_STREAM (object);
+void
+oss_stream_add_control (OssStream *stream, OssStreamControl *control)
+{
+ const gchar *name;
+
+ g_return_if_fail (OSS_IS_STREAM (stream));
+ g_return_if_fail (OSS_IS_STREAM_CONTROL (control));
- g_hash_table_destroy (stream->priv->controls);
+ name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control));
+
+ stream->priv->controls =
+ g_list_append (stream->priv->controls, g_object_ref (control));
- G_OBJECT_CLASS (oss_stream_parent_class)->finalize (object);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-added",
+ name);
}
-OssStream *
-oss_stream_new (const gchar *name,
- MateMixerDevice *device,
- MateMixerStreamFlags flags)
+void
+oss_stream_load (OssStream *stream)
{
- OssStream *stream;
+ GList *list;
+
+ g_return_if_fail (OSS_IS_STREAM (stream));
+
+ list = stream->priv->controls;
+ while (list != NULL) {
+ OssStreamControl *control = OSS_STREAM_CONTROL (list->data);
- stream = g_object_new (OSS_TYPE_STREAM,
- "name", name,
- "device", device,
- "flags", flags,
- NULL);
- return stream;
+ oss_stream_control_load (control);
+ list = list->next;
+ }
+
+ if (stream->priv->swtch != NULL)
+ oss_switch_load (stream->priv->swtch);
}
gboolean
-oss_stream_add_control (OssStream *stream, OssStreamControl *control)
+oss_stream_has_controls (OssStream *stream)
{
- const gchar *name;
-
g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE);
- g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE);
- name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (control));
+ if (stream->priv->controls != NULL)
+ return TRUE;
- g_hash_table_insert (stream->priv->controls,
- g_strdup (name),
- control);
- return TRUE;
+ return FALSE;
}
gboolean
-oss_stream_set_default_control (OssStream *stream, OssStreamControl *control)
+oss_stream_has_default_control (OssStream *stream)
{
g_return_val_if_fail (OSS_IS_STREAM (stream), FALSE);
- g_return_val_if_fail (OSS_IS_STREAM_CONTROL (control), FALSE);
-
- /* This function is only used internally so avoid validating that the control
- * belongs to this stream */
- if (stream->priv->control != NULL)
- g_object_unref (stream->priv->control);
- if G_LIKELY (control != NULL)
- stream->priv->control = g_object_ref (control);
- else
- stream->priv->control = NULL;
+ if (mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream)) != NULL)
+ return TRUE;
- return TRUE;
+ return FALSE;
}
-static MateMixerStreamControl *
-oss_stream_get_control (MateMixerStream *mms, const gchar *name)
+OssStreamControl *
+oss_stream_get_default_control (OssStream *stream)
{
- g_return_val_if_fail (OSS_IS_STREAM (mms), NULL);
- g_return_val_if_fail (name != NULL, NULL);
+ MateMixerStreamControl *control;
+
+ g_return_val_if_fail (OSS_IS_STREAM (stream), NULL);
+
+ control = mate_mixer_stream_get_default_control (MATE_MIXER_STREAM (stream));
+ if (control != NULL)
+ return OSS_STREAM_CONTROL (control);
- return g_hash_table_lookup (OSS_STREAM (mms)->priv->controls, name);
+ return NULL;
}
-static MateMixerStreamControl *
-oss_stream_get_default_control (MateMixerStream *mms)
+void
+oss_stream_set_default_control (OssStream *stream, OssStreamControl *control)
{
- g_return_val_if_fail (OSS_IS_STREAM (mms), NULL);
+ g_return_if_fail (OSS_IS_STREAM (stream));
+ g_return_if_fail (control == NULL || OSS_IS_STREAM_CONTROL (control));
+
+ if (control == NULL)
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream), NULL);
+ else
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (stream),
+ MATE_MIXER_STREAM_CONTROL (control));
+}
- return MATE_MIXER_STREAM_CONTROL (OSS_STREAM (mms)->priv->control);
+void
+oss_stream_set_switch_data (OssStream *stream, gint fd, GList *options)
+{
+ g_return_if_fail (OSS_IS_STREAM (stream));
+ g_return_if_fail (fd != -1);
+ g_return_if_fail (options != NULL);
+
+ /* Function may only be called once */
+ if G_UNLIKELY (stream->priv->swtch != NULL) {
+ g_warn_if_reached ();
+ return;
+ }
+
+ /* Takes ownership of options */
+ stream->priv->swtch = oss_switch_new (OSS_STREAM_SWITCH_NAME,
+ _("Capture Source"),
+ fd,
+ options);
+
+ /* Read the active selection */
+ oss_switch_load (stream->priv->swtch);
+
+ stream->priv->switches = g_list_prepend (NULL, g_object_ref (stream->priv->swtch));
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-added",
+ OSS_STREAM_SWITCH_NAME);
}
-static GList *
-oss_stream_list_controls (MateMixerStream *mms)
+void
+oss_stream_remove_all (OssStream *stream)
{
GList *list;
+ g_return_if_fail (OSS_IS_STREAM (stream));
+
+ list = stream->priv->controls;
+ while (list != NULL) {
+ MateMixerStreamControl *control = MATE_MIXER_STREAM_CONTROL (list->data);
+ GList *next = list->next;
+
+ oss_stream_control_close (OSS_STREAM_CONTROL (control));
+
+ stream->priv->controls = g_list_delete_link (stream->priv->controls, list);
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "control-removed",
+ mate_mixer_stream_control_get_name (control));
+
+ g_object_unref (control);
+ list = next;
+ }
+
+ /* Unset the default stream control */
+ oss_stream_set_default_control (stream, NULL);
+
+ if (stream->priv->swtch != NULL) {
+ oss_switch_close (stream->priv->swtch);
+
+ g_list_free_full (stream->priv->switches, g_object_unref);
+ stream->priv->switches = NULL;
+
+ g_signal_emit_by_name (G_OBJECT (stream),
+ "switch-removed",
+ OSS_STREAM_SWITCH_NAME);
+
+ g_clear_object (&stream->priv->swtch);
+ }
+}
+
+static const GList *
+oss_stream_list_controls (MateMixerStream *mms)
+{
g_return_val_if_fail (OSS_IS_STREAM (mms), NULL);
- /* Convert the hash table to a linked list, this list is expected to be
- * cached in the main library */
- list = g_hash_table_get_values (OSS_STREAM (mms)->priv->controls);
- if (list != NULL)
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
+ return OSS_STREAM (mms)->priv->controls;
+}
+
+static const GList *
+oss_stream_list_switches (MateMixerStream *mms)
+{
+ g_return_val_if_fail (OSS_IS_STREAM (mms), NULL);
- return list;
+ return OSS_STREAM (mms)->priv->switches;
}
diff --git a/backends/oss/oss-stream.h b/backends/oss/oss-stream.h
index 0470eb7..2ade0d8 100644
--- a/backends/oss/oss-stream.h
+++ b/backends/oss/oss-stream.h
@@ -22,8 +22,7 @@
#include <glib-object.h>
#include <libmatemixer/matemixer.h>
-#include "oss-device.h"
-#include "oss-stream-control.h"
+#include "oss-types.h"
G_BEGIN_DECLS
@@ -40,7 +39,6 @@ G_BEGIN_DECLS
#define OSS_STREAM_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_STREAM, OssStreamClass))
-typedef struct _OssStream OssStream;
typedef struct _OssStreamClass OssStreamClass;
typedef struct _OssStreamPrivate OssStreamPrivate;
@@ -57,17 +55,29 @@ struct _OssStreamClass
MateMixerStreamClass parent;
};
-GType oss_stream_get_type (void) G_GNUC_CONST;
+GType oss_stream_get_type (void) G_GNUC_CONST;
-OssStream *oss_stream_new (const gchar *name,
- MateMixerDevice *device,
- MateMixerStreamFlags flags);
+OssStream * oss_stream_new (const gchar *name,
+ MateMixerDevice *device,
+ MateMixerDirection direction);
-gboolean oss_stream_add_control (OssStream *stream,
- OssStreamControl *control);
+void oss_stream_add_control (OssStream *stream,
+ OssStreamControl *control);
-gboolean oss_stream_set_default_control (OssStream *stream,
- OssStreamControl *control);
+void oss_stream_load (OssStream *stream);
+
+gboolean oss_stream_has_controls (OssStream *stream);
+gboolean oss_stream_has_default_control (OssStream *stream);
+
+OssStreamControl *oss_stream_get_default_control (OssStream *stream);
+void oss_stream_set_default_control (OssStream *stream,
+ OssStreamControl *control);
+
+void oss_stream_set_switch_data (OssStream *stream,
+ gint fd,
+ GList *options);
+
+void oss_stream_remove_all (OssStream *stream);
G_END_DECLS
diff --git a/backends/oss/oss-switch-option.c b/backends/oss/oss-switch-option.c
new file mode 100644
index 0000000..862133d
--- /dev/null
+++ b/backends/oss/oss-switch-option.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "oss-switch-option.h"
+
+struct _OssSwitchOptionPrivate
+{
+ guint devnum;
+};
+
+static void oss_switch_option_class_init (OssSwitchOptionClass *klass);
+static void oss_switch_option_init (OssSwitchOption *option);
+
+G_DEFINE_TYPE (OssSwitchOption, oss_switch_option, MATE_MIXER_TYPE_SWITCH_OPTION)
+
+static void
+oss_switch_option_class_init (OssSwitchOptionClass *klass)
+{
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (OssSwitchOptionPrivate));
+}
+
+static void
+oss_switch_option_init (OssSwitchOption *option)
+{
+ option->priv = G_TYPE_INSTANCE_GET_PRIVATE (option,
+ OSS_TYPE_SWITCH_OPTION,
+ OssSwitchOptionPrivate);
+}
+
+OssSwitchOption *
+oss_switch_option_new (const gchar *name,
+ const gchar *label,
+ const gchar *icon,
+ guint devnum)
+{
+ OssSwitchOption *option;
+
+ option = g_object_new (OSS_TYPE_SWITCH_OPTION,
+ "name", name,
+ "label", label,
+ "icon", icon,
+ NULL);
+
+ option->priv->devnum = devnum;
+ return option;
+}
+
+guint
+oss_switch_option_get_devnum (OssSwitchOption *option)
+{
+ g_return_val_if_fail (OSS_IS_SWITCH_OPTION (option), 0);
+
+ return option->priv->devnum;
+}
diff --git a/backends/oss/oss-switch-option.h b/backends/oss/oss-switch-option.h
new file mode 100644
index 0000000..91c21e3
--- /dev/null
+++ b/backends/oss/oss-switch-option.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OSS_SWITCH_OPTION_H
+#define OSS_SWITCH_OPTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "oss-types.h"
+
+G_BEGIN_DECLS
+
+#define OSS_TYPE_SWITCH_OPTION \
+ (oss_switch_option_get_type ())
+#define OSS_SWITCH_OPTION(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_SWITCH_OPTION, OssSwitchOption))
+#define OSS_IS_SWITCH_OPTION(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_SWITCH_OPTION))
+#define OSS_SWITCH_OPTION_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_SWITCH_OPTION, OssSwitchOptionClass))
+#define OSS_IS_SWITCH_OPTION_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_SWITCH_OPTION))
+#define OSS_SWITCH_OPTION_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_SWITCH_OPTION, OssSwitchOptionClass))
+
+typedef struct _OssSwitchOptionClass OssSwitchOptionClass;
+typedef struct _OssSwitchOptionPrivate OssSwitchOptionPrivate;
+
+struct _OssSwitchOption
+{
+ MateMixerSwitchOption parent;
+
+ /*< private >*/
+ OssSwitchOptionPrivate *priv;
+};
+
+struct _OssSwitchOptionClass
+{
+ MateMixerSwitchOptionClass parent_class;
+};
+
+GType oss_switch_option_get_type (void) G_GNUC_CONST;
+
+OssSwitchOption *oss_switch_option_new (const gchar *name,
+ const gchar *label,
+ const gchar *icon,
+ guint devnum);
+
+guint oss_switch_option_get_devnum (OssSwitchOption *option);
+
+G_END_DECLS
+
+#endif /* OSS_SWITCH_OPTION_H */
diff --git a/backends/oss/oss-switch.c b/backends/oss/oss-switch.c
new file mode 100644
index 0000000..b138051
--- /dev/null
+++ b/backends/oss/oss-switch.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "oss-common.h"
+#include "oss-switch.h"
+#include "oss-switch-option.h"
+
+struct _OssSwitchPrivate
+{
+ gint fd;
+ GList *options;
+};
+
+static void oss_switch_class_init (OssSwitchClass *klass);
+static void oss_switch_init (OssSwitch *swtch);
+static void oss_switch_dispose (GObject *object);
+static void oss_switch_finalize (GObject *object);
+
+G_DEFINE_TYPE (OssSwitch, oss_switch, MATE_MIXER_TYPE_SWITCH)
+
+static gboolean oss_switch_set_active_option (MateMixerSwitch *mms,
+ MateMixerSwitchOption *mmso);
+
+static const GList *oss_switch_list_options (MateMixerSwitch *mms);
+
+static void
+oss_switch_class_init (OssSwitchClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerSwitchClass *switch_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = oss_switch_dispose;
+ object_class->finalize = oss_switch_finalize;
+
+ switch_class = MATE_MIXER_SWITCH_CLASS (klass);
+ switch_class->set_active_option = oss_switch_set_active_option;
+ switch_class->list_options = oss_switch_list_options;
+
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (OssSwitchPrivate));
+}
+
+static void
+oss_switch_init (OssSwitch *swtch)
+{
+ swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch,
+ OSS_TYPE_SWITCH,
+ OssSwitchPrivate);
+}
+
+static void
+oss_switch_dispose (GObject *object)
+{
+ OssSwitch *swtch;
+
+ swtch = OSS_SWITCH (object);
+
+ if (swtch->priv->options != NULL) {
+ g_list_free_full (swtch->priv->options, g_object_unref);
+ swtch->priv->options = NULL;
+ }
+
+ G_OBJECT_CLASS (oss_switch_parent_class)->dispose (object);
+}
+
+static void
+oss_switch_finalize (GObject *object)
+{
+ OssSwitch *swtch;
+
+ swtch = OSS_SWITCH (object);
+
+ if (swtch->priv->fd != -1)
+ close (swtch->priv->fd);
+
+ G_OBJECT_CLASS (oss_switch_parent_class)->finalize (object);
+}
+
+OssSwitch *
+oss_switch_new (const gchar *name,
+ const gchar *label,
+ gint fd,
+ GList *options)
+{
+ OssSwitch *swtch;
+
+ swtch = g_object_new (OSS_TYPE_SWITCH,
+ "name", name,
+ "label", label,
+ "flags", MATE_MIXER_SWITCH_ALLOWS_NO_ACTIVE_OPTION,
+ "role", MATE_MIXER_SWITCH_ROLE_PORT,
+ NULL);
+
+ /* Takes ownership of options */
+ swtch->priv->fd = dup (fd);
+ swtch->priv->options = options;
+
+ return swtch;
+}
+
+void
+oss_switch_load (OssSwitch *swtch)
+{
+ GList *list;
+ gint recsrc;
+ gint ret;
+
+ g_return_if_fail (OSS_IS_SWITCH (swtch));
+
+ if G_UNLIKELY (swtch->priv->fd == -1)
+ return;
+
+ ret = ioctl (swtch->priv->fd, MIXER_READ (SOUND_MIXER_RECSRC), &recsrc);
+ if (ret == -1)
+ return;
+ if (recsrc == 0) {
+ _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch), NULL);
+ return;
+ }
+
+ list = swtch->priv->options;
+ while (list != NULL) {
+ OssSwitchOption *option = OSS_SWITCH_OPTION (list->data);
+ gint devnum = oss_switch_option_get_devnum (option);
+
+ /* Mark the selected option when we find it */
+ if (recsrc & (1 << devnum)) {
+ _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch),
+ MATE_MIXER_SWITCH_OPTION (option));
+ return;
+ }
+ list = list->next;
+ }
+
+ g_warning ("Unknown active option of switch %s",
+ mate_mixer_switch_get_name (MATE_MIXER_SWITCH (swtch)));
+}
+
+void
+oss_switch_close (OssSwitch *swtch)
+{
+ g_return_if_fail (OSS_IS_SWITCH (swtch));
+
+ if (swtch->priv->fd == -1)
+ return;
+
+ close (swtch->priv->fd);
+ swtch->priv->fd = -1;
+}
+
+static gboolean
+oss_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso)
+{
+ OssSwitch *swtch;
+ gint ret;
+ gint recsrc;
+ gint devnum;
+
+ g_return_val_if_fail (OSS_IS_SWITCH (mms), FALSE);
+ g_return_val_if_fail (OSS_IS_SWITCH_OPTION (mmso), FALSE);
+
+ swtch = OSS_SWITCH (mms);
+
+ if G_UNLIKELY (swtch->priv->fd == -1)
+ return FALSE;
+
+ devnum = oss_switch_option_get_devnum (OSS_SWITCH_OPTION (mmso));
+ recsrc = 1 << devnum;
+
+ ret = ioctl (swtch->priv->fd, MIXER_WRITE (SOUND_MIXER_RECSRC), &recsrc);
+ if (ret == -1)
+ return FALSE;
+
+ return TRUE;
+}
+
+static const GList *
+oss_switch_list_options (MateMixerSwitch *mms)
+{
+ g_return_val_if_fail (OSS_IS_SWITCH (mms), NULL);
+
+ return OSS_SWITCH (mms)->priv->options;
+}
diff --git a/backends/oss/oss-switch.h b/backends/oss/oss-switch.h
new file mode 100644
index 0000000..dc91758
--- /dev/null
+++ b/backends/oss/oss-switch.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OSS_SWITCH_H
+#define OSS_SWITCH_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "oss-types.h"
+
+G_BEGIN_DECLS
+
+#define OSS_TYPE_SWITCH \
+ (oss_switch_get_type ())
+#define OSS_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), OSS_TYPE_SWITCH, OssSwitch))
+#define OSS_IS_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), OSS_TYPE_SWITCH))
+#define OSS_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), OSS_TYPE_SWITCH, OssSwitchClass))
+#define OSS_IS_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), OSS_TYPE_SWITCH))
+#define OSS_SWITCH_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), OSS_TYPE_SWITCH, OssSwitchClass))
+
+typedef struct _OssSwitchClass OssSwitchClass;
+typedef struct _OssSwitchPrivate OssSwitchPrivate;
+
+struct _OssSwitch
+{
+ MateMixerSwitch parent;
+
+ /*< private >*/
+ OssSwitchPrivate *priv;
+};
+
+struct _OssSwitchClass
+{
+ MateMixerSwitchClass parent_class;
+};
+
+GType oss_switch_get_type (void) G_GNUC_CONST;
+
+OssSwitch *oss_switch_new (const gchar *name,
+ const gchar *label,
+ gint fd,
+ GList *options);
+
+void oss_switch_load (OssSwitch *swtch);
+void oss_switch_close (OssSwitch *swtch);
+
+G_END_DECLS
+
+#endif /* OSS_SWITCH_H */
diff --git a/backends/oss/oss-types.h b/backends/oss/oss-types.h
new file mode 100644
index 0000000..afd4f58
--- /dev/null
+++ b/backends/oss/oss-types.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef OSS_TYPES_H
+#define OSS_TYPES_H
+
+G_BEGIN_DECLS
+
+typedef struct _OssBackend OssBackend;
+typedef struct _OssDevice OssDevice;
+typedef struct _OssStream OssStream;
+typedef struct _OssStreamControl OssStreamControl;
+typedef struct _OssSwitch OssSwitch;
+typedef struct _OssSwitchOption OssSwitchOption;
+
+G_END_DECLS
+
+#endif /* OSS_TYPES_H */
diff --git a/backends/pulse/Makefile.am b/backends/pulse/Makefile.am
index 0e5a4d6..4c851bf 100644
--- a/backends/pulse/Makefile.am
+++ b/backends/pulse/Makefile.am
@@ -3,6 +3,7 @@ backenddir = $(libdir)/libmatemixer
backend_LTLIBRARIES = libmatemixer-pulse.la
AM_CPPFLAGS = \
+ -Wno-unknown-pragmas \
-I$(top_srcdir) \
-DG_LOG_DOMAIN=\"libmatemixer-pulse\"
@@ -13,12 +14,14 @@ libmatemixer_pulse_la_CFLAGS = \
libmatemixer_pulse_la_SOURCES = \
pulse-backend.c \
pulse-backend.h \
- pulse-client-stream.c \
- pulse-client-stream.h \
pulse-connection.c \
pulse-connection.h \
pulse-device.c \
pulse-device.h \
+ pulse-device-profile.c \
+ pulse-device-profile.h \
+ pulse-device-switch.c \
+ pulse-device-switch.h \
pulse-enums.h \
pulse-enum-types.c \
pulse-enum-types.h \
@@ -28,16 +31,31 @@ libmatemixer_pulse_la_SOURCES = \
pulse-helpers.h \
pulse-monitor.c \
pulse-monitor.h \
+ pulse-port.c \
+ pulse-port.h \
+ pulse-port-switch.c \
+ pulse-port-switch.h \
pulse-stream.c \
pulse-stream.h \
+ pulse-stream-control.c \
+ pulse-stream-control.h \
pulse-sink.c \
pulse-sink.h \
+ pulse-sink-control.c \
+ pulse-sink-control.h \
pulse-sink-input.c \
pulse-sink-input.h \
+ pulse-sink-switch.c \
+ pulse-sink-switch.h \
pulse-source.c \
pulse-source.h \
+ pulse-source-control.c \
+ pulse-source-control.h \
pulse-source-output.c \
- pulse-source-output.h
+ pulse-source-output.h \
+ pulse-source-switch.c \
+ pulse-source-switch.h \
+ pulse-types.h
libmatemixer_pulse_la_LIBADD = \
$(GLIB_LIBS) \
diff --git a/backends/pulse/pulse-backend.c b/backends/pulse/pulse-backend.c
index 0494545..e1b7ed5 100644
--- a/backends/pulse/pulse-backend.c
+++ b/backends/pulse/pulse-backend.c
@@ -19,9 +19,8 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-backend.h>
-#include <libmatemixer/matemixer-backend-module.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include <pulse/ext-stream-restore.h>
@@ -38,72 +37,110 @@
#include "pulse-source-output.h"
#define BACKEND_NAME "PulseAudio"
-#define BACKEND_PRIORITY 10
+#define BACKEND_PRIORITY 100
struct _PulseBackendPrivate
{
- gchar *app_name;
- gchar *app_id;
- gchar *app_version;
- gchar *app_icon;
- gchar *server_address;
- gboolean connected_once;
- GSource *connect_source;
- MateMixerStream *default_sink;
- MateMixerStream *default_source;
- GHashTable *devices;
- GHashTable *streams;
- GHashTable *ext_streams;
- MateMixerState state;
- PulseConnection *connection;
-};
-
-enum {
- PROP_0,
- PROP_STATE,
- PROP_DEFAULT_INPUT,
- PROP_DEFAULT_OUTPUT,
- N_PROPERTIES
+ guint connect_tag;
+ gboolean connected_once;
+ GHashTable *devices;
+ GHashTable *sinks;
+ GHashTable *sources;
+ GHashTable *sink_inputs;
+ GHashTable *source_outputs;
+ GHashTable *ext_streams;
+ GList *devices_list;
+ GList *streams_list;
+ GList *ext_streams_list;
+ MateMixerAppInfo *app_info;
+ gchar *server_address;
+ PulseConnection *connection;
};
-static void mate_mixer_backend_interface_init (MateMixerBackendInterface *iface);
+#define PULSE_CHANGE_STATE(p, s) \
+ (_mate_mixer_backend_set_state (MATE_MIXER_BACKEND (p), (s)))
+#define PULSE_GET_DEFAULT_SINK(p) \
+ (mate_mixer_backend_get_default_output_stream (MATE_MIXER_BACKEND (p)))
+#define PULSE_GET_DEFAULT_SOURCE(p) \
+ (mate_mixer_backend_get_default_input_stream (MATE_MIXER_BACKEND (p)))
+#define PULSE_SET_DEFAULT_SINK(p, s) \
+ (_mate_mixer_backend_set_default_output_stream (MATE_MIXER_BACKEND (p), MATE_MIXER_STREAM (s)))
+#define PULSE_SET_DEFAULT_SOURCE(p, s) \
+ (_mate_mixer_backend_set_default_input_stream (MATE_MIXER_BACKEND (p), MATE_MIXER_STREAM (s)))
+
+#define PULSE_GET_PENDING_SINK(p) \
+ (g_object_get_data (G_OBJECT (p), \
+ "__matemixer_pulse_pending_sink")) \
+
+#define PULSE_SET_PENDING_SINK(p,name) \
+ (g_object_set_data_full (G_OBJECT (p), \
+ "__matemixer_pulse_pending_sink", \
+ g_strdup (name), \
+ g_free))
+
+#define PULSE_SET_PENDING_SINK_NULL(p) \
+ (g_object_set_data (G_OBJECT (p), \
+ "__matemixer_pulse_pending_sink", \
+ NULL))
+
+#define PULSE_GET_PENDING_SOURCE(p) \
+ (g_object_get_data (G_OBJECT (p), \
+ "__matemixer_pulse_pending_source")) \
+
+#define PULSE_SET_PENDING_SOURCE(p,name) \
+ (g_object_set_data_full (G_OBJECT (p), \
+ "__matemixer_pulse_pending_source", \
+ g_strdup (name), \
+ g_free))
+
+#define PULSE_SET_PENDING_SOURCE_NULL(p) \
+ (g_object_set_data (G_OBJECT (p), \
+ "__matemixer_pulse_pending_source", \
+ NULL))
+
+#define PULSE_GET_HANGING(o) \
+ ((gboolean) g_object_get_data (G_OBJECT (o), \
+ "__matemixer_pulse_hanging"))
+
+#define PULSE_SET_HANGING(o) \
+ (g_object_set_data (G_OBJECT (o), \
+ "__matemixer_pulse_hanging", \
+ GUINT_TO_POINTER (1)))
+
+#define PULSE_UNSET_HANGING(o) \
+ (g_object_steal_data (G_OBJECT (o), \
+ "__matemixer_pulse_hanging"))
static void pulse_backend_class_init (PulseBackendClass *klass);
static void pulse_backend_class_finalize (PulseBackendClass *klass);
-static void pulse_backend_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec);
-
static void pulse_backend_init (PulseBackend *pulse);
static void pulse_backend_dispose (GObject *object);
static void pulse_backend_finalize (GObject *object);
-G_DEFINE_DYNAMIC_TYPE_EXTENDED (PulseBackend, pulse_backend,
- G_TYPE_OBJECT, 0,
- G_IMPLEMENT_INTERFACE_DYNAMIC (MATE_MIXER_TYPE_BACKEND,
- mate_mixer_backend_interface_init))
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+G_DEFINE_DYNAMIC_TYPE (PulseBackend, pulse_backend, MATE_MIXER_TYPE_BACKEND)
+#pragma clang diagnostic pop
-static gboolean pulse_backend_open (MateMixerBackend *backend);
-static void pulse_backend_close (MateMixerBackend *backend);
+static gboolean pulse_backend_open (MateMixerBackend *backend);
+static void pulse_backend_close (MateMixerBackend *backend);
-static MateMixerState pulse_backend_get_state (MateMixerBackend *backend);
+static void pulse_backend_set_app_info (MateMixerBackend *backend,
+ MateMixerAppInfo *info);
-static void pulse_backend_set_data (MateMixerBackend *backend,
- const MateMixerBackendData *data);
+static void pulse_backend_set_server_address (MateMixerBackend *backend,
+ const gchar *address);
-static GList * pulse_backend_list_devices (MateMixerBackend *backend);
-static GList * pulse_backend_list_streams (MateMixerBackend *backend);
-static GList * pulse_backend_list_cached_streams (MateMixerBackend *backend);
+static const GList * pulse_backend_list_devices (MateMixerBackend *backend);
+static const GList * pulse_backend_list_streams (MateMixerBackend *backend);
+static const GList * pulse_backend_list_stored_controls (MateMixerBackend *backend);
-static MateMixerStream *pulse_backend_get_default_input_stream (MateMixerBackend *backend);
-static gboolean pulse_backend_set_default_input_stream (MateMixerBackend *backend,
- MateMixerStream *stream);
+static gboolean pulse_backend_set_default_input_stream (MateMixerBackend *backend,
+ MateMixerStream *stream);
-static MateMixerStream *pulse_backend_get_default_output_stream (MateMixerBackend *backend);
-static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend,
- MateMixerStream *stream);
+static gboolean pulse_backend_set_default_output_stream (MateMixerBackend *backend,
+ MateMixerStream *stream);
static void on_connection_state_notify (PulseConnection *connection,
GParamSpec *pspec,
@@ -152,32 +189,16 @@ static void on_connection_ext_stream_info (PulseConnection
PulseBackend *pulse);
static gboolean connect_source_reconnect (PulseBackend *pulse);
-static void connect_source_remove (PulseBackend *pulse);
static void check_pending_sink (PulseBackend *pulse,
PulseStream *stream);
static void check_pending_source (PulseBackend *pulse,
PulseStream *stream);
-static void mark_hanging (PulseBackend *pulse);
-static void mark_hanging_hash (GHashTable *hash);
-
-static void unmark_hanging (PulseBackend *pulse,
- GObject *object);
-
-static void remove_hanging (PulseBackend *pulse);
-static void remove_device (PulseBackend *pulse,
- PulseDevice *device);
-static void remove_stream (PulseBackend *pulse,
- PulseStream *stream);
-
-static void change_state (PulseBackend *backend,
- MateMixerState state);
+static void free_list_devices (PulseBackend *pulse);
+static void free_list_streams (PulseBackend *pulse);
+static void free_list_ext_streams (PulseBackend *pulse);
-static gint compare_devices (gconstpointer a,
- gconstpointer b);
-static gint compare_streams (gconstpointer a,
- gconstpointer b);
static gboolean compare_stream_names (gpointer key,
gpointer value,
gpointer user_data);
@@ -195,78 +216,42 @@ backend_module_init (GTypeModule *module)
info.backend_type = MATE_MIXER_BACKEND_PULSEAUDIO;
}
-const MateMixerBackendInfo *
-backend_module_get_info (void)
+const MateMixerBackendInfo *backend_module_get_info (void)
{
return &info;
}
static void
-mate_mixer_backend_interface_init (MateMixerBackendInterface *iface)
-{
- iface->open = pulse_backend_open;
- iface->close = pulse_backend_close;
- iface->get_state = pulse_backend_get_state;
- iface->set_data = pulse_backend_set_data;
- iface->list_devices = pulse_backend_list_devices;
- iface->list_streams = pulse_backend_list_streams;
- iface->list_cached_streams = pulse_backend_list_cached_streams;
- iface->get_default_input_stream = pulse_backend_get_default_input_stream;
- iface->set_default_input_stream = pulse_backend_set_default_input_stream;
- iface->get_default_output_stream = pulse_backend_get_default_output_stream;
- iface->set_default_output_stream = pulse_backend_set_default_output_stream;
-}
-
-static void
pulse_backend_class_init (PulseBackendClass *klass)
{
- GObjectClass *object_class;
+ GObjectClass *object_class;
+ MateMixerBackendClass *backend_class;
object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = pulse_backend_dispose;
- object_class->finalize = pulse_backend_finalize;
- object_class->get_property = pulse_backend_get_property;
-
- g_object_class_override_property (object_class, PROP_STATE, "state");
- g_object_class_override_property (object_class, PROP_DEFAULT_INPUT, "default-input");
- g_object_class_override_property (object_class, PROP_DEFAULT_OUTPUT, "default-output");
+ object_class->dispose = pulse_backend_dispose;
+ object_class->finalize = pulse_backend_finalize;
+
+ backend_class = MATE_MIXER_BACKEND_CLASS (klass);
+ backend_class->set_app_info = pulse_backend_set_app_info;
+ backend_class->set_server_address = pulse_backend_set_server_address;
+ backend_class->open = pulse_backend_open;
+ backend_class->close = pulse_backend_close;
+ backend_class->list_devices = pulse_backend_list_devices;
+ backend_class->list_streams = pulse_backend_list_streams;
+ backend_class->list_stored_controls = pulse_backend_list_stored_controls;
+ backend_class->set_default_input_stream = pulse_backend_set_default_input_stream;
+ backend_class->set_default_output_stream = pulse_backend_set_default_output_stream;
g_type_class_add_private (object_class, sizeof (PulseBackendPrivate));
}
-/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE_EXTENDED() */
+/* Called in the code generated by G_DEFINE_DYNAMIC_TYPE() */
static void
pulse_backend_class_finalize (PulseBackendClass *klass)
{
}
static void
-pulse_backend_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec)
-{
- PulseBackend *pulse;
-
- pulse = PULSE_BACKEND (object);
-
- switch (param_id) {
- case PROP_STATE:
- g_value_set_enum (value, pulse->priv->state);
- break;
- case PROP_DEFAULT_INPUT:
- g_value_set_object (value, pulse->priv->default_source);
- break;
- case PROP_DEFAULT_OUTPUT:
- g_value_set_object (value, pulse->priv->default_sink);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
- break;
- }
-}
-
-static void
pulse_backend_init (PulseBackend *pulse)
{
pulse->priv = G_TYPE_INSTANCE_GET_PRIVATE (pulse,
@@ -279,22 +264,46 @@ pulse_backend_init (PulseBackend *pulse)
g_direct_equal,
NULL,
g_object_unref);
- pulse->priv->streams =
- g_hash_table_new_full (g_int64_hash,
- g_int64_equal,
- g_free,
+ pulse->priv->sinks =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
g_object_unref);
+ pulse->priv->sources =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+
pulse->priv->ext_streams =
g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
g_object_unref);
+
+ pulse->priv->sink_inputs =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+ pulse->priv->source_outputs =
+ g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
}
static void
pulse_backend_dispose (GObject *object)
{
- pulse_backend_close (MATE_MIXER_BACKEND (object));
+ MateMixerBackend *backend;
+ MateMixerState state;
+
+ backend = MATE_MIXER_BACKEND (object);
+
+ state = mate_mixer_backend_get_state (backend);
+ if (state != MATE_MIXER_STATE_IDLE)
+ pulse_backend_close (backend);
G_OBJECT_CLASS (pulse_backend_parent_class)->dispose (object);
}
@@ -306,19 +315,24 @@ pulse_backend_finalize (GObject *object)
pulse = PULSE_BACKEND (object);
- g_free (pulse->priv->app_name);
- g_free (pulse->priv->app_id);
- g_free (pulse->priv->app_version);
- g_free (pulse->priv->app_icon);
- g_free (pulse->priv->server_address);
+ if (pulse->priv->app_info != NULL)
+ _mate_mixer_app_info_free (pulse->priv->app_info);
- g_hash_table_destroy (pulse->priv->devices);
- g_hash_table_destroy (pulse->priv->streams);
- g_hash_table_destroy (pulse->priv->ext_streams);
+ g_hash_table_unref (pulse->priv->devices);
+ g_hash_table_unref (pulse->priv->sinks);
+ g_hash_table_unref (pulse->priv->sources);
+ g_hash_table_unref (pulse->priv->ext_streams);
+ g_hash_table_unref (pulse->priv->sink_inputs);
+ g_hash_table_unref (pulse->priv->source_outputs);
G_OBJECT_CLASS (pulse_backend_parent_class)->finalize (object);
}
+#define PULSE_APP_NAME(p) (mate_mixer_app_info_get_name (p->priv->app_info))
+#define PULSE_APP_ID(p) (mate_mixer_app_info_get_id (p->priv->app_info))
+#define PULSE_APP_VERSION(p) (mate_mixer_app_info_get_version (p->priv->app_info))
+#define PULSE_APP_ICON(p) (mate_mixer_app_info_get_icon (p->priv->app_info))
+
static gboolean
pulse_backend_open (MateMixerBackend *backend)
{
@@ -329,22 +343,22 @@ pulse_backend_open (MateMixerBackend *backend)
pulse = PULSE_BACKEND (backend);
- if (G_UNLIKELY (pulse->priv->connection != NULL)) {
+ if G_UNLIKELY (pulse->priv->connection != NULL) {
g_warn_if_reached ();
return TRUE;
}
- connection = pulse_connection_new (pulse->priv->app_name,
- pulse->priv->app_id,
- pulse->priv->app_version,
- pulse->priv->app_icon,
+ connection = pulse_connection_new (PULSE_APP_NAME (pulse),
+ PULSE_APP_ID (pulse),
+ PULSE_APP_VERSION (pulse),
+ PULSE_APP_ICON (pulse),
pulse->priv->server_address);
/* No connection attempt is made during the construction of the connection,
* but it sets up the PulseAudio structures, which might fail in an
* unlikely case */
- if (G_UNLIKELY (connection == NULL)) {
- change_state (pulse, MATE_MIXER_STATE_FAILED);
+ if G_UNLIKELY (connection == NULL) {
+ PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED);
return FALSE;
}
@@ -409,16 +423,21 @@ pulse_backend_open (MateMixerBackend *backend)
G_CALLBACK (on_connection_ext_stream_info),
pulse);
- change_state (pulse, MATE_MIXER_STATE_CONNECTING);
+ PULSE_CHANGE_STATE (backend, MATE_MIXER_STATE_CONNECTING);
/* Connect to the PulseAudio server, this might fail either instantly or
* asynchronously, for example when remote connection timeouts */
if (pulse_connection_connect (connection, FALSE) == FALSE) {
g_object_unref (connection);
- change_state (pulse, MATE_MIXER_STATE_FAILED);
+ PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED);
return FALSE;
}
+ _mate_mixer_backend_set_flags (backend,
+ MATE_MIXER_BACKEND_HAS_APPLICATION_CONTROLS |
+ MATE_MIXER_BACKEND_CAN_SET_DEFAULT_INPUT_STREAM |
+ MATE_MIXER_BACKEND_CAN_SET_DEFAULT_OUTPUT_STREAM);
+
pulse->priv->connection = connection;
return TRUE;
}
@@ -432,7 +451,10 @@ pulse_backend_close (MateMixerBackend *backend)
pulse = PULSE_BACKEND (backend);
- connect_source_remove (pulse);
+ if (pulse->priv->connect_tag != 0) {
+ g_source_remove (pulse->priv->connect_tag);
+ pulse->priv->connect_tag = 0;
+ }
if (pulse->priv->connection != NULL) {
g_signal_handlers_disconnect_by_data (G_OBJECT (pulse->priv->connection),
@@ -441,109 +463,100 @@ pulse_backend_close (MateMixerBackend *backend)
g_clear_object (&pulse->priv->connection);
}
- g_clear_object (&pulse->priv->default_sink);
- g_clear_object (&pulse->priv->default_source);
-
g_hash_table_remove_all (pulse->priv->devices);
- g_hash_table_remove_all (pulse->priv->streams);
+ g_hash_table_remove_all (pulse->priv->sinks);
+ g_hash_table_remove_all (pulse->priv->sources);
g_hash_table_remove_all (pulse->priv->ext_streams);
pulse->priv->connected_once = FALSE;
- change_state (pulse, MATE_MIXER_STATE_IDLE);
+ PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_IDLE);
}
-static MateMixerState
-pulse_backend_get_state (MateMixerBackend *backend)
+static void
+pulse_backend_set_app_info (MateMixerBackend *backend, MateMixerAppInfo *info)
{
- g_return_val_if_fail (PULSE_IS_BACKEND (backend), MATE_MIXER_STATE_UNKNOWN);
+ PulseBackend *pulse;
+
+ g_return_if_fail (PULSE_IS_BACKEND (backend));
+ g_return_if_fail (info != NULL);
+
+ pulse = PULSE_BACKEND (backend);
- return PULSE_BACKEND (backend)->priv->state;
+ if (pulse->priv->app_info != NULL)
+ _mate_mixer_app_info_free (pulse->priv->app_info);
+
+ pulse->priv->app_info = _mate_mixer_app_info_copy (info);
}
static void
-pulse_backend_set_data (MateMixerBackend *backend, const MateMixerBackendData *data)
+pulse_backend_set_server_address (MateMixerBackend *backend, const gchar *address)
{
- PulseBackend *pulse;
-
g_return_if_fail (PULSE_IS_BACKEND (backend));
- g_return_if_fail (data != NULL);
- pulse = PULSE_BACKEND (backend);
+ g_free (PULSE_BACKEND (backend)->priv->server_address);
- g_free (pulse->priv->app_name);
- g_free (pulse->priv->app_id);
- g_free (pulse->priv->app_version);
- g_free (pulse->priv->app_icon);
- g_free (pulse->priv->server_address);
-
- pulse->priv->app_name = g_strdup (data->app_name);
- pulse->priv->app_id = g_strdup (data->app_id);
- pulse->priv->app_version = g_strdup (data->app_version);
- pulse->priv->app_icon = g_strdup (data->app_icon);
- pulse->priv->server_address = g_strdup (data->server_address);
+ PULSE_BACKEND (backend)->priv->server_address = g_strdup (address);
}
-static GList *
+static const GList *
pulse_backend_list_devices (MateMixerBackend *backend)
{
- GList *list;
+ PulseBackend *pulse;
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
- /* Convert the hash table to a sorted linked list, this list is expected
- * to be cached in the main library */
- list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->devices);
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
+ pulse = PULSE_BACKEND (backend);
- return g_list_sort (list, compare_devices);
+ if (pulse->priv->devices_list == NULL) {
+ pulse->priv->devices_list = g_hash_table_get_values (pulse->priv->devices);
+ if (pulse->priv->devices_list != NULL)
+ g_list_foreach (pulse->priv->devices_list, (GFunc) g_object_ref, NULL);
}
- return NULL;
+ return pulse->priv->devices_list;
}
-static GList *
+static const GList *
pulse_backend_list_streams (MateMixerBackend *backend)
{
- GList *list;
+ PulseBackend *pulse;
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
- /* Convert the hash table to a sorted linked list, this list is expected
- * to be cached in the main library */
- list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->streams);
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- return g_list_sort (list, compare_streams);
- }
- return NULL;
-}
+ pulse = PULSE_BACKEND (backend);
-static GList *
-pulse_backend_list_cached_streams (MateMixerBackend *backend)
-{
- GList *list;
+ if (pulse->priv->streams_list == NULL) {
+ GList *sinks;
+ GList *sources;
- g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
+ sinks = g_hash_table_get_values (pulse->priv->sinks);
+ if (sinks != NULL)
+ g_list_foreach (sinks, (GFunc) g_object_ref, NULL);
- /* Convert the hash table to a sorted linked list, this list is expected
- * to be cached in the main library */
- list = g_hash_table_get_values (PULSE_BACKEND (backend)->priv->ext_streams);
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
+ sources = g_hash_table_get_values (pulse->priv->sources);
+ if (sources != NULL)
+ g_list_foreach (sources, (GFunc) g_object_ref, NULL);
- return g_list_sort (list, compare_streams);
+ pulse->priv->streams_list = g_list_concat (sinks, sources);
}
- return NULL;
+ return pulse->priv->streams_list;
}
-static MateMixerStream *
-pulse_backend_get_default_input_stream (MateMixerBackend *backend)
+static const GList *
+pulse_backend_list_stored_controls (MateMixerBackend *backend)
{
+ PulseBackend *pulse;
+
g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
- return PULSE_BACKEND (backend)->priv->default_source;
+ pulse = PULSE_BACKEND (backend);
+
+ if (pulse->priv->ext_streams_list == NULL) {
+ pulse->priv->ext_streams_list = g_hash_table_get_values (pulse->priv->ext_streams);
+ if (pulse->priv->ext_streams_list != NULL)
+ g_list_foreach (pulse->priv->ext_streams_list, (GFunc) g_object_ref, NULL);
+ }
+ return pulse->priv->ext_streams_list;
}
static gboolean
@@ -562,29 +575,13 @@ pulse_backend_set_default_input_stream (MateMixerBackend *backend,
if (pulse_connection_set_default_source (pulse->priv->connection, name) == FALSE)
return FALSE;
- if (pulse->priv->default_source != NULL)
- g_object_unref (pulse->priv->default_source);
-
- pulse->priv->default_source = g_object_ref (stream);
-
/* We might be in the process of setting a default source for which the details
* are not yet known, make sure the change does not happen */
- g_object_set_data (G_OBJECT (pulse),
- "backend-pending-source",
- NULL);
-
- g_object_notify (G_OBJECT (pulse), "default-input");
+ PULSE_SET_PENDING_SOURCE_NULL (pulse);
+ PULSE_SET_DEFAULT_SOURCE (pulse, stream);
return TRUE;
}
-static MateMixerStream *
-pulse_backend_get_default_output_stream (MateMixerBackend *backend)
-{
- g_return_val_if_fail (PULSE_IS_BACKEND (backend), NULL);
-
- return PULSE_BACKEND (backend)->priv->default_sink;
-}
-
static gboolean
pulse_backend_set_default_output_stream (MateMixerBackend *backend,
MateMixerStream *stream)
@@ -601,18 +598,10 @@ pulse_backend_set_default_output_stream (MateMixerBackend *backend,
if (pulse_connection_set_default_sink (pulse->priv->connection, name) == FALSE)
return FALSE;
- if (pulse->priv->default_sink != NULL)
- g_object_unref (pulse->priv->default_sink);
-
- pulse->priv->default_sink = g_object_ref (stream);
-
/* We might be in the process of setting a default sink for which the details
* are not yet known, make sure the change does not happen */
- g_object_set_data (G_OBJECT (pulse),
- "backend-pending-sink",
- NULL);
-
- g_object_notify (G_OBJECT (pulse), "default-output");
+ PULSE_SET_PENDING_SINK_NULL (pulse);
+ PULSE_SET_DEFAULT_SINK (pulse, stream);
return TRUE;
}
@@ -633,41 +622,41 @@ on_connection_state_notify (PulseConnection *connection,
* Stream callbacks will unmark available streams and remaining
* unavailable streams will be removed when the CONNECTED state
* is reached. */
- mark_hanging (pulse);
- change_state (pulse, MATE_MIXER_STATE_CONNECTING);
+ PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_CONNECTING);
- if (pulse->priv->connect_source == NULL &&
- pulse_connection_connect (connection, TRUE) == FALSE) {
- pulse->priv->connect_source = g_timeout_source_new (200);
+ if G_UNLIKELY (pulse->priv->connect_tag != 0)
+ break;
- g_source_set_callback (pulse->priv->connect_source,
+ if (pulse_connection_connect (connection, TRUE) == FALSE) {
+ GSource *source;
+
+ source = g_timeout_source_new (200);
+ g_source_set_callback (source,
(GSourceFunc) connect_source_reconnect,
pulse,
- (GDestroyNotify) connect_source_remove);
+ NULL);
+ pulse->priv->connect_tag =
+ g_source_attach (source, g_main_context_get_thread_default ());
- g_source_attach (pulse->priv->connect_source,
- g_main_context_get_thread_default ());
+ g_source_unref (source);
}
break;
}
/* First connection attempt has failed */
- change_state (pulse, MATE_MIXER_STATE_FAILED);
+ PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_FAILED);
break;
case PULSE_CONNECTION_CONNECTING:
case PULSE_CONNECTION_AUTHORIZING:
case PULSE_CONNECTION_LOADING:
- change_state (pulse, MATE_MIXER_STATE_CONNECTING);
+ PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_CONNECTING);
break;
case PULSE_CONNECTION_CONNECTED:
- if (pulse->priv->connected_once == TRUE)
- remove_hanging (pulse);
- else
- pulse->priv->connected_once = TRUE;
+ pulse->priv->connected_once = TRUE;
- change_state (pulse, MATE_MIXER_STATE_READY);
+ PULSE_CHANGE_STATE (pulse, MATE_MIXER_STATE_READY);
break;
}
}
@@ -677,18 +666,17 @@ on_connection_server_info (PulseConnection *connection,
const pa_server_info *info,
PulseBackend *pulse)
{
- const gchar *name_source = NULL;
- const gchar *name_sink = NULL;
+ MateMixerStream *stream;
+ const gchar *name_source = NULL;
+ const gchar *name_sink = NULL;
- if (pulse->priv->default_source != NULL)
- name_source = mate_mixer_stream_get_name (pulse->priv->default_source);
+ stream = PULSE_GET_DEFAULT_SOURCE (pulse);
+ if (stream != NULL)
+ name_source = mate_mixer_stream_get_name (stream);
if (g_strcmp0 (name_source, info->default_source_name) != 0) {
- if (pulse->priv->default_source != NULL)
- g_clear_object (&pulse->priv->default_source);
-
if (info->default_source_name != NULL) {
- MateMixerStream *stream = g_hash_table_find (pulse->priv->streams,
+ MateMixerStream *stream = g_hash_table_find (pulse->priv->sources,
compare_stream_names,
(gpointer) info->default_source_name);
@@ -698,20 +686,14 @@ on_connection_server_info (PulseConnection *connection,
* When this happens, remember the name of the stream and wait for the
* stream info callback. */
if (stream != NULL) {
- pulse->priv->default_source = g_object_ref (stream);
- g_object_set_data (G_OBJECT (pulse),
- "backend-pending-source",
- NULL);
-
- g_debug ("Default input stream changed to %s", info->default_source_name);
+ PULSE_SET_DEFAULT_SOURCE (pulse, stream);
+ PULSE_SET_PENDING_SOURCE_NULL (pulse);
} else {
g_debug ("Default input stream changed to unknown stream %s",
info->default_source_name);
- g_object_set_data_full (G_OBJECT (pulse),
- "backend-pending-source",
- g_strdup (info->default_source_name),
- g_free);
+ PULSE_SET_PENDING_SOURCE (pulse, info->default_source_name);
+ PULSE_SET_DEFAULT_SOURCE (pulse, NULL);
/* In most cases (for example changing profile) the stream info
* arrives by itself, but do not rely on it and request it explicitely.
@@ -721,20 +703,16 @@ on_connection_server_info (PulseConnection *connection,
info->default_source_name);
}
} else
- g_debug ("Default input stream unset");
-
- g_object_notify (G_OBJECT (pulse), "default-input");
+ PULSE_SET_DEFAULT_SOURCE (pulse, NULL);
}
- if (pulse->priv->default_sink != NULL)
- name_sink = mate_mixer_stream_get_name (pulse->priv->default_sink);
+ stream = PULSE_GET_DEFAULT_SINK (pulse);
+ if (stream != NULL)
+ name_sink = mate_mixer_stream_get_name (stream);
if (g_strcmp0 (name_sink, info->default_sink_name) != 0) {
- if (pulse->priv->default_sink != NULL)
- g_clear_object (&pulse->priv->default_sink);
-
if (info->default_sink_name != NULL) {
- MateMixerStream *stream = g_hash_table_find (pulse->priv->streams,
+ MateMixerStream *stream = g_hash_table_find (pulse->priv->sinks,
compare_stream_names,
(gpointer) info->default_sink_name);
@@ -744,21 +722,14 @@ on_connection_server_info (PulseConnection *connection,
* When this happens, remember the name of the stream and wait for the
* stream info callback. */
if (stream != NULL) {
- pulse->priv->default_sink = g_object_ref (stream);
- g_object_set_data (G_OBJECT (pulse),
- "backend-pending-sink",
- NULL);
-
- g_debug ("Default output stream changed to %s", info->default_sink_name);
-
+ PULSE_SET_DEFAULT_SINK (pulse, stream);
+ PULSE_SET_PENDING_SINK_NULL (pulse);
} else {
g_debug ("Default output stream changed to unknown stream %s",
info->default_sink_name);
- g_object_set_data_full (G_OBJECT (pulse),
- "backend-pending-sink",
- g_strdup (info->default_sink_name),
- g_free);
+ PULSE_SET_PENDING_SINK (pulse, info->default_sink_name);
+ PULSE_SET_DEFAULT_SINK (pulse, NULL);
/* In most cases (for example changing profile) the stream info
* arrives by itself, but do not rely on it and request it explicitely.
@@ -768,12 +739,10 @@ on_connection_server_info (PulseConnection *connection,
info->default_sink_name);
}
} else
- g_debug ("Default output stream unset");
-
- g_object_notify (G_OBJECT (pulse), "default-output");
+ PULSE_SET_DEFAULT_SINK (pulse, NULL);
}
- if (pulse->priv->state != MATE_MIXER_STATE_READY)
+ if (mate_mixer_backend_get_state (MATE_MIXER_BACKEND (pulse)) != MATE_MIXER_STATE_READY)
g_debug ("Sound server is %s version %s, running on %s",
info->server_name,
info->server_version,
@@ -790,21 +759,17 @@ on_connection_card_info (PulseConnection *connection,
device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->index));
if (device == NULL) {
device = pulse_device_new (connection, info);
- if (G_UNLIKELY (device == NULL))
- return;
- g_hash_table_insert (pulse->priv->devices, GUINT_TO_POINTER (info->index), device);
+ g_hash_table_insert (pulse->priv->devices,
+ GUINT_TO_POINTER (info->index),
+ device);
+ free_list_devices (pulse);
g_signal_emit_by_name (G_OBJECT (pulse),
"device-added",
mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
- } else {
+ } else
pulse_device_update (device, info);
-
- /* The object might be hanging if reconnecting is in progress, remove the
- * hanging flag to prevent it from being removed when connected */
- unmark_hanging (pulse, G_OBJECT (device));
- }
}
static void
@@ -813,29 +778,22 @@ on_connection_card_removed (PulseConnection *connection,
PulseBackend *pulse)
{
PulseDevice *device;
+ gchar *name;
device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (index));
- if (G_UNLIKELY (device == NULL))
+ if G_UNLIKELY (device == NULL)
return;
- remove_device (pulse, device);
-}
+ name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
-/* PulseAudio uses 32-bit integers as indices for sinks, sink inputs, sources and
- * source inputs, these indices are not unique among the different kinds of streams,
- * but we want to keep all of them in a single hash table. Allow this by using 64-bit
- * hash table keys, use the lower 32 bits for the PulseAudio index and some of the
- * higher bits to indicate what kind of stream it is. */
-enum {
- HASH_BIT_SINK = (1ULL << 63),
- HASH_BIT_SINK_INPUT = (1ULL << 62),
- HASH_BIT_SOURCE = (1ULL << 61),
- HASH_BIT_SOURCE_OUTPUT = (1ULL << 60)
-};
-#define HASH_ID_SINK(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK)
-#define HASH_ID_SINK_INPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SINK_INPUT)
-#define HASH_ID_SOURCE(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE)
-#define HASH_ID_SOURCE_OUTPUT(idx) (((idx) & 0xffffffff) | HASH_BIT_SOURCE_OUTPUT)
+ g_hash_table_remove (pulse->priv->devices, GUINT_TO_POINTER (index));
+
+ free_list_devices (pulse);
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "device-removed",
+ name);
+ g_free (name);
+}
static void
on_connection_sink_info (PulseConnection *connection,
@@ -844,32 +802,36 @@ on_connection_sink_info (PulseConnection *connection,
{
PulseDevice *device = NULL;
PulseStream *stream;
- gint64 index = HASH_ID_SINK (info->index);
if (info->card != PA_INVALID_INDEX)
- device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card));
+ device = g_hash_table_lookup (pulse->priv->devices,
+ GUINT_TO_POINTER (info->card));
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ stream = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (info->index));
if (stream == NULL) {
- stream = pulse_sink_new (connection, info, device);
- if (G_UNLIKELY (stream == NULL))
- return;
+ stream = PULSE_STREAM (pulse_sink_new (connection, info, device));
- g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+ free_list_streams (pulse);
+ g_hash_table_insert (pulse->priv->sinks,
+ GUINT_TO_POINTER (info->index),
+ stream);
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-added",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ if (device != NULL) {
+ pulse_device_add_stream (device, stream);
+ } else {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+ /* Only emit when not a part of the device, otherwise emitted by
+ * the main library */
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-added",
+ name);
+ }
/* We might be waiting for this sink to set it as the default */
check_pending_sink (pulse, stream);
- } else {
- pulse_sink_update (stream, info, device);
-
- /* The object might be hanging if reconnecting is in progress, remove the
- * hanging flag to prevent it from being removed when connected */
- unmark_hanging (pulse, G_OBJECT (stream));
- }
+ } else
+ pulse_sink_update (PULSE_SINK (stream), info);
}
static void
@@ -878,13 +840,38 @@ on_connection_sink_removed (PulseConnection *connection,
PulseBackend *pulse)
{
PulseStream *stream;
- gint64 index = HASH_ID_SINK (idx);
+ PulseDevice *device;
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
- if (G_UNLIKELY (stream == NULL))
+ stream = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (idx));
+ if G_UNLIKELY (stream == NULL)
return;
- remove_stream (pulse, stream);
+ g_object_ref (stream);
+
+ free_list_streams (pulse);
+ g_hash_table_remove (pulse->priv->sinks, GUINT_TO_POINTER (idx));
+
+ device = pulse_stream_get_device (stream);
+ if (device != NULL) {
+ pulse_device_remove_stream (device, stream);
+ } else {
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-removed",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ }
+
+ /* The removed stream might be one of the default streams, this happens
+ * especially when switching profiles, after which PulseAudio removes the
+ * old streams and creates new ones with different names */
+ if (MATE_MIXER_STREAM (stream) == PULSE_GET_DEFAULT_SINK (pulse)) {
+ PULSE_SET_DEFAULT_SINK (pulse, NULL);
+
+ /* PulseAudio usually sends a server info update by itself when default
+ * stream changes, but there is at least one case when it does not - setting
+ * a card profile to off, so to be sure request an update explicitely */
+ pulse_connection_load_server_info (pulse->priv->connection);
+ }
+ g_object_unref (stream);
}
static void
@@ -892,40 +879,20 @@ on_connection_sink_input_info (PulseConnection *connection,
const pa_sink_input_info *info,
PulseBackend *pulse)
{
- PulseStream *stream;
- PulseStream *parent = NULL;
- gint64 index;
+ PulseSink *sink;
- if (G_LIKELY (info->sink != PA_INVALID_INDEX)) {
- index = HASH_ID_SINK (info->sink);
-
- parent = g_hash_table_lookup (pulse->priv->streams, &index);
- if (G_UNLIKELY (parent == NULL))
- g_debug ("Unknown parent %d of PulseAudio sink input %s",
- info->sink,
- info->name);
- }
-
- index = HASH_ID_SINK_INPUT (info->index);
-
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
- if (stream == NULL) {
- stream = pulse_sink_input_new (connection, info, parent);
- if (G_UNLIKELY (stream == NULL))
- return;
+ if G_UNLIKELY (info->sink == PA_INVALID_INDEX)
+ return;
- g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+ sink = g_hash_table_lookup (pulse->priv->sinks, GUINT_TO_POINTER (info->sink));
+ if G_UNLIKELY (sink == NULL)
+ return;
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-added",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
- } else {
- pulse_sink_input_update (stream, info, parent);
+ pulse_sink_add_input (sink, info);
- /* The object might be hanging if reconnecting is in progress, remove the
- * hanging flag to prevent it from being removed when connected */
- unmark_hanging (pulse, G_OBJECT (stream));
- }
+ g_hash_table_insert (pulse->priv->sink_inputs,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (sink));
}
static void
@@ -933,14 +900,13 @@ on_connection_sink_input_removed (PulseConnection *connection,
guint idx,
PulseBackend *pulse)
{
- PulseStream *stream;
- gint64 index = HASH_ID_SINK_INPUT (idx);
+ PulseSink *sink;
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
- if (G_UNLIKELY (stream == NULL))
+ sink = g_hash_table_lookup (pulse->priv->sink_inputs, GUINT_TO_POINTER (idx));
+ if G_UNLIKELY (sink == NULL)
return;
- remove_stream (pulse, stream);
+ pulse_sink_remove_input (sink, idx);
}
static void
@@ -950,36 +916,40 @@ on_connection_source_info (PulseConnection *connection,
{
PulseDevice *device = NULL;
PulseStream *stream;
- gint64 index = HASH_ID_SOURCE (info->index);
/* Skip monitor streams */
if (info->monitor_of_sink != PA_INVALID_INDEX)
return;
if (info->card != PA_INVALID_INDEX)
- device = g_hash_table_lookup (pulse->priv->devices, GUINT_TO_POINTER (info->card));
+ device = g_hash_table_lookup (pulse->priv->devices,
+ GUINT_TO_POINTER (info->card));
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
+ stream = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (info->index));
if (stream == NULL) {
- stream = pulse_source_new (connection, info, device);
- if (G_UNLIKELY (stream == NULL))
- return;
+ stream = PULSE_STREAM (pulse_source_new (connection, info, device));
- g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+ free_list_streams (pulse);
+ g_hash_table_insert (pulse->priv->sources,
+ GUINT_TO_POINTER (info->index),
+ stream);
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-added",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ if (device != NULL) {
+ pulse_device_add_stream (device, stream);
+ } else {
+ const gchar *name =
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+ /* Only emit when not a part of the device, otherwise emitted by
+ * the main library */
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-added",
+ name);
+ }
/* We might be waiting for this source to set it as the default */
check_pending_source (pulse, stream);
- } else {
- pulse_source_update (stream, info, device);
-
- /* The object might be hanging if reconnecting is in progress, remove the
- * hanging flag to prevent it from being removed when connected */
- unmark_hanging (pulse, G_OBJECT (stream));
- }
+ } else
+ pulse_source_update (PULSE_SOURCE (stream), info);
}
static void
@@ -987,14 +957,39 @@ on_connection_source_removed (PulseConnection *connection,
guint idx,
PulseBackend *pulse)
{
+ PulseDevice *device;
PulseStream *stream;
- gint64 index = HASH_ID_SOURCE (idx);
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
- if (G_UNLIKELY (stream == NULL))
+ stream = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (idx));
+ if G_UNLIKELY (stream == NULL)
return;
- remove_stream (pulse, stream);
+ g_object_ref (stream);
+
+ free_list_streams (pulse);
+ g_hash_table_remove (pulse->priv->sources, GUINT_TO_POINTER (idx));
+
+ device = pulse_stream_get_device (stream);
+ if (device != NULL) {
+ pulse_device_remove_stream (device, stream);
+ } else {
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stream-removed",
+ mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ }
+
+ /* The removed stream might be one of the default streams, this happens
+ * especially when switching profiles, after which PulseAudio removes the
+ * old streams and creates new ones with different names */
+ if (MATE_MIXER_STREAM (stream) == PULSE_GET_DEFAULT_SOURCE (pulse)) {
+ PULSE_SET_DEFAULT_SOURCE (pulse, NULL);
+
+ /* PulseAudio usually sends a server info update by itself when default
+ * stream changes, but there is at least one case when it does not - setting
+ * a card profile to off, so to be sure request an update explicitely */
+ pulse_connection_load_server_info (pulse->priv->connection);
+ }
+ g_object_unref (stream);
}
static void
@@ -1002,39 +997,20 @@ on_connection_source_output_info (PulseConnection *connection,
const pa_source_output_info *info,
PulseBackend *pulse)
{
- PulseStream *stream;
- PulseStream *parent = NULL;
- gint64 index;
-
- if (G_LIKELY (info->source != PA_INVALID_INDEX)) {
- index = HASH_ID_SOURCE (info->source);
-
- /* Most likely a monitor source that we have skipped */
- parent = g_hash_table_lookup (pulse->priv->streams, &index);
- if (parent == NULL)
- return;
- }
+ PulseSource *source;
- index = HASH_ID_SOURCE_OUTPUT (info->index);
-
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
- if (stream == NULL) {
- stream = pulse_source_output_new (connection, info, parent);
- if (G_UNLIKELY (stream == NULL))
- return;
+ if G_UNLIKELY (info->source == PA_INVALID_INDEX)
+ return;
- g_hash_table_insert (pulse->priv->streams, g_memdup (&index, 8), stream);
+ source = g_hash_table_lookup (pulse->priv->sources, GUINT_TO_POINTER (info->source));
+ if G_UNLIKELY (source == NULL)
+ return;
- g_signal_emit_by_name (G_OBJECT (pulse),
- "stream-added",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
- } else {
- pulse_source_output_update (stream, info, parent);
+ pulse_source_add_output (source, info);
- /* The object might be hanging if reconnecting is in progress, remove the
- * hanging flag to prevent it from being removed when connected */
- unmark_hanging (pulse, G_OBJECT (stream));
- }
+ g_hash_table_insert (pulse->priv->source_outputs,
+ GUINT_TO_POINTER (info->index),
+ g_object_ref (source));
}
static void
@@ -1042,14 +1018,13 @@ on_connection_source_output_removed (PulseConnection *connection,
guint idx,
PulseBackend *pulse)
{
- PulseStream *stream;
- gint64 index = HASH_ID_SOURCE_OUTPUT (idx);
+ PulseSource *source;
- stream = g_hash_table_lookup (pulse->priv->streams, &index);
- if (G_UNLIKELY (stream == NULL))
+ source = g_hash_table_lookup (pulse->priv->source_outputs, GUINT_TO_POINTER (idx));
+ if G_UNLIKELY (source == NULL)
return;
- remove_stream (pulse, stream);
+ pulse_source_remove_output (source, idx);
}
static void
@@ -1057,63 +1032,75 @@ on_connection_ext_stream_info (PulseConnection *connection,
const pa_ext_stream_restore_info *info,
PulseBackend *pulse)
{
- PulseStream *stream;
- PulseStream *parent = NULL;
+ PulseExtStream *ext;
+ PulseStream *parent = NULL;
- if (G_LIKELY (info->device != NULL))
- parent = g_hash_table_find (pulse->priv->streams, compare_stream_names,
+ if (info->device != NULL) {
+ parent = g_hash_table_find (pulse->priv->sinks, compare_stream_names,
(gpointer) info->device);
- stream = g_hash_table_lookup (pulse->priv->ext_streams, info->name);
- if (stream == NULL) {
- stream = pulse_ext_stream_new (connection, info, parent);
- if (G_UNLIKELY (stream == NULL))
- return;
+ if (parent == NULL)
+ parent = g_hash_table_find (pulse->priv->sources, compare_stream_names,
+ (gpointer) info->device);
+ }
+
+ ext = g_hash_table_lookup (pulse->priv->ext_streams, info->name);
+ if (ext == NULL) {
+ ext = pulse_ext_stream_new (connection, info, parent);
- g_hash_table_insert (pulse->priv->ext_streams, g_strdup (info->name), stream);
+ free_list_ext_streams (pulse);
+ g_hash_table_insert (pulse->priv->ext_streams,
+ g_strdup (info->name),
+ ext);
g_signal_emit_by_name (G_OBJECT (pulse),
- "cached-stream-added",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
+ "stored-control-added",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (ext)));
} else {
- pulse_ext_stream_update (stream, info, parent);
+ pulse_ext_stream_update (ext, info, parent);
/* The object might be hanging if ext-streams are being loaded, remove
* the hanging flag to prevent it from being removed */
- unmark_hanging (pulse, G_OBJECT (stream));
+ PULSE_UNSET_HANGING (ext);
}
}
static void
on_connection_ext_stream_loading (PulseConnection *connection, PulseBackend *pulse)
{
- mark_hanging_hash (pulse->priv->ext_streams);
+ GHashTableIter iter;
+ gpointer ext;
+
+ g_hash_table_iter_init (&iter, pulse->priv->ext_streams);
+
+ while (g_hash_table_iter_next (&iter, NULL, &ext) == TRUE)
+ PULSE_SET_HANGING (ext);
}
static void
on_connection_ext_stream_loaded (PulseConnection *connection, PulseBackend *pulse)
{
GHashTableIter iter;
- gpointer value;
+ gpointer name;
+ gpointer ext;
g_hash_table_iter_init (&iter, pulse->priv->ext_streams);
- while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) {
- guint hanging =
- GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging"));
+ while (g_hash_table_iter_next (&iter, &name, &ext) == TRUE) {
+ if (PULSE_GET_HANGING (ext) == FALSE)
+ continue;
- if (hanging == 1) {
- gchar *name = g_strdup ((const gchar *) value);
+ free_list_ext_streams (pulse);
+ g_hash_table_remove (pulse->priv->ext_streams, (gconstpointer) name);
- g_hash_table_remove (pulse->priv->ext_streams, (gconstpointer) name);
- g_signal_emit_by_name (G_OBJECT (pulse),
- "cached-stream-removed",
- name);
- g_free (name);
- }
+ g_signal_emit_by_name (G_OBJECT (pulse),
+ "stored-control-removed",
+ name);
}
+ g_debug ("Ext-streams refreshed");
}
+// XXX rename
static gboolean
connect_source_reconnect (PulseBackend *pulse)
{
@@ -1121,16 +1108,10 @@ connect_source_reconnect (PulseBackend *pulse)
* and wait for the connection state notifications, otherwise this function
* will be called again */
if (pulse_connection_connect (pulse->priv->connection, TRUE) == TRUE) {
- connect_source_remove (pulse);
- return FALSE;
+ pulse->priv->connect_tag = 0;
+ return G_SOURCE_REMOVE;
}
- return TRUE;
-}
-
-static void
-connect_source_remove (PulseBackend *pulse)
-{
- g_clear_pointer (&pulse->priv->connect_source, g_source_unref);
+ return G_SOURCE_CONTINUE;
}
static void
@@ -1141,7 +1122,7 @@ check_pending_sink (PulseBackend *pulse, PulseStream *stream)
/* See if the currently added sream matches the default input stream
* we are waiting for */
- pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-sink");
+ pending = PULSE_GET_PENDING_SINK (pulse);
if (pending == NULL)
return;
@@ -1149,14 +1130,10 @@ check_pending_sink (PulseBackend *pulse, PulseStream *stream)
if (g_strcmp0 (pending, name) != 0)
return;
- pulse->priv->default_sink = g_object_ref (stream);
- g_object_set_data (G_OBJECT (pulse),
- "backend-pending-sink",
- NULL);
+ g_debug ("Setting default output stream to pending stream %s", name);
- g_debug ("Default output stream changed to pending stream %s", name);
-
- g_object_notify (G_OBJECT (pulse), "default-output");
+ PULSE_SET_PENDING_SINK_NULL (pulse);
+ PULSE_SET_DEFAULT_SINK (pulse, stream);
}
static void
@@ -1167,7 +1144,7 @@ check_pending_source (PulseBackend *pulse, PulseStream *stream)
/* See if the currently added sream matches the default input stream
* we are waiting for */
- pending = g_object_get_data (G_OBJECT (pulse), "backend-pending-source");
+ pending = PULSE_GET_PENDING_SOURCE (pulse);
if (pending == NULL)
return;
@@ -1175,160 +1152,43 @@ check_pending_source (PulseBackend *pulse, PulseStream *stream)
if (g_strcmp0 (pending, name) != 0)
return;
- pulse->priv->default_source = g_object_ref (stream);
- g_object_set_data (G_OBJECT (pulse),
- "backend-pending-source",
- NULL);
-
- g_debug ("Default input stream changed to pending stream %s", name);
+ g_debug ("Setting default input stream to pending stream %s", name);
- g_object_notify (G_OBJECT (pulse), "default-input");
+ PULSE_SET_PENDING_SOURCE_NULL (pulse);
+ PULSE_SET_DEFAULT_SOURCE (pulse, stream);
}
static void
-mark_hanging (PulseBackend *pulse)
+free_list_devices (PulseBackend *pulse)
{
- /* Mark devices and streams as hanging, ext-streams are handled separately */
- mark_hanging_hash (pulse->priv->devices);
- mark_hanging_hash (pulse->priv->streams);
-}
-
-static void
-mark_hanging_hash (GHashTable *hash)
-{
- GHashTableIter iter;
- gpointer value;
-
- g_hash_table_iter_init (&iter, hash);
-
- while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE)
- g_object_set_data (G_OBJECT (value), "backend-hanging", GUINT_TO_POINTER (1));
-}
-
-static void
-unmark_hanging (PulseBackend *pulse, GObject *object)
-{
- if (pulse->priv->connected_once == FALSE)
- return;
- if (pulse->priv->state == MATE_MIXER_STATE_READY)
+ if (pulse->priv->devices_list == NULL)
return;
- g_object_steal_data (object, "backend-hanging");
-}
-
-static void
-remove_hanging (PulseBackend *pulse)
-{
- GHashTableIter iter;
- gpointer value;
-
- g_hash_table_iter_init (&iter, pulse->priv->devices);
-
- while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) {
- guint hanging =
- GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging"));
-
- if (hanging == 1)
- remove_device (pulse, PULSE_DEVICE (value));
- }
-
- g_hash_table_iter_init (&iter, pulse->priv->streams);
-
- while (g_hash_table_iter_next (&iter, NULL, &value) == TRUE) {
- guint hanging =
- GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (value), "backend-hanging"));
+ g_list_free_full (pulse->priv->devices_list, g_object_unref);
- if (hanging == 1)
- remove_stream (pulse, PULSE_STREAM (value));
- }
-}
-
-static void
-remove_device (PulseBackend *pulse, PulseDevice *device)
-{
- gchar *name;
-
- name = g_strdup (mate_mixer_device_get_name (MATE_MIXER_DEVICE (device)));
-
- g_hash_table_remove (pulse->priv->devices,
- GUINT_TO_POINTER (pulse_device_get_index (device)));
-
- g_signal_emit_by_name (G_OBJECT (pulse), "device-removed", name);
- g_free (name);
+ pulse->priv->devices_list = NULL;
}
static void
-remove_stream (PulseBackend *pulse, PulseStream *stream)
+free_list_streams (PulseBackend *pulse)
{
- gchar *name;
- guint32 idx;
- gint64 index;
- gboolean reload = FALSE;
-
- /* The removed stream might be one of the default streams, this happens
- * especially when switching profiles, after which PulseAudio removes the
- * old streams and creates new ones with different names */
- if (MATE_MIXER_STREAM (stream) == pulse->priv->default_sink) {
- g_clear_object (&pulse->priv->default_sink);
-
- g_object_notify (G_OBJECT (pulse), "default-output");
- reload = TRUE;
- }
- else if (MATE_MIXER_STREAM (stream) == pulse->priv->default_source) {
- g_clear_object (&pulse->priv->default_source);
-
- g_object_notify (G_OBJECT (pulse), "default-input");
- reload = TRUE;
- }
-
- idx = pulse_stream_get_index (stream);
-
- if (PULSE_IS_SINK (stream))
- index = HASH_ID_SINK (idx);
- else if (PULSE_IS_SINK_INPUT (stream))
- index = HASH_ID_SINK_INPUT (idx);
- else if (PULSE_IS_SOURCE (stream))
- index = HASH_ID_SOURCE (idx);
- else
- index = HASH_ID_SOURCE_OUTPUT (idx);
-
- name = g_strdup (mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream)));
-
- g_hash_table_remove (pulse->priv->streams, &index);
+ if (pulse->priv->streams_list == NULL)
+ return;
- /* PulseAudio usually sends a server info update by itself when default
- * stream changes, but there is at least one case when it does not - setting
- * a card profile to off, so to be sure request an update explicitely */
- if (reload == TRUE)
- pulse_connection_load_server_info (pulse->priv->connection);
+ g_list_free_full (pulse->priv->streams_list, g_object_unref);
- g_signal_emit_by_name (G_OBJECT (pulse), "stream-removed", name);
- g_free (name);
+ pulse->priv->streams_list = NULL;
}
static void
-change_state (PulseBackend *backend, MateMixerState state)
+free_list_ext_streams (PulseBackend *pulse)
{
- if (backend->priv->state == state)
+ if (pulse->priv->ext_streams_list == NULL)
return;
- backend->priv->state = state;
-
- g_object_notify (G_OBJECT (backend), "state");
-}
+ g_list_free_full (pulse->priv->ext_streams_list, g_object_unref);
-static gint
-compare_devices (gconstpointer a, gconstpointer b)
-{
- return strcmp (mate_mixer_device_get_name (MATE_MIXER_DEVICE (a)),
- mate_mixer_device_get_name (MATE_MIXER_DEVICE (b)));
-}
-
-static gint
-compare_streams (gconstpointer a, gconstpointer b)
-{
- return strcmp (mate_mixer_stream_get_name (MATE_MIXER_STREAM (a)),
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (b)));
+ pulse->priv->ext_streams_list = NULL;
}
static gboolean
@@ -1336,5 +1196,5 @@ compare_stream_names (gpointer key, gpointer value, gpointer user_data)
{
MateMixerStream *stream = MATE_MIXER_STREAM (value);
- return !strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data);
+ return strcmp (mate_mixer_stream_get_name (stream), (const gchar *) user_data) == 0;
}
diff --git a/backends/pulse/pulse-backend.h b/backends/pulse/pulse-backend.h
index bd1face..2c4b8a8 100644
--- a/backends/pulse/pulse-backend.h
+++ b/backends/pulse/pulse-backend.h
@@ -20,8 +20,10 @@
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
-#include <libmatemixer/matemixer-backend-module.h>
+#include "pulse-types.h"
#define PULSE_TYPE_BACKEND \
(pulse_backend_get_type ())
@@ -36,13 +38,12 @@
#define PULSE_BACKEND_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_BACKEND, PulseBackendClass))
-typedef struct _PulseBackend PulseBackend;
typedef struct _PulseBackendClass PulseBackendClass;
typedef struct _PulseBackendPrivate PulseBackendPrivate;
struct _PulseBackend
{
- GObject parent;
+ MateMixerBackend parent;
/*< private >*/
PulseBackendPrivate *priv;
@@ -50,7 +51,7 @@ struct _PulseBackend
struct _PulseBackendClass
{
- GObjectClass parent_class;
+ MateMixerBackendClass parent_class;
};
GType pulse_backend_get_type (void) G_GNUC_CONST;
diff --git a/backends/pulse/pulse-client-stream.c b/backends/pulse/pulse-client-stream.c
deleted file mode 100644
index b99c498..0000000
--- a/backends/pulse/pulse-client-stream.c
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright (C) 2014 Michal Ratajsky <[email protected]>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-#include <glib.h>
-#include <glib-object.h>
-
-#include <libmatemixer/matemixer-client-stream.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
-
-#include <pulse/pulseaudio.h>
-
-#include "pulse-client-stream.h"
-#include "pulse-sink.h"
-#include "pulse-source.h"
-#include "pulse-stream.h"
-
-struct _PulseClientStreamPrivate
-{
- gchar *app_name;
- gchar *app_id;
- gchar *app_version;
- gchar *app_icon;
- MateMixerStream *parent;
- MateMixerClientStreamFlags flags;
- MateMixerClientStreamRole role;
-};
-
-enum {
- PROP_0,
- PROP_CLIENT_FLAGS,
- PROP_ROLE,
- PROP_PARENT,
- PROP_APP_NAME,
- PROP_APP_ID,
- PROP_APP_VERSION,
- PROP_APP_ICON
-};
-
-enum {
- REMOVED,
- N_SIGNALS
-};
-
-static guint signals[N_SIGNALS] = { 0, };
-
-static void mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface);
-
-static void pulse_client_stream_class_init (PulseClientStreamClass *klass);
-
-static void pulse_client_stream_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec);
-
-static void pulse_client_stream_init (PulseClientStream *client);
-static void pulse_client_stream_dispose (GObject *object);
-static void pulse_client_stream_finalize (GObject *object);
-
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseClientStream, pulse_client_stream, PULSE_TYPE_STREAM,
- G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_CLIENT_STREAM,
- mate_mixer_client_stream_interface_init))
-
-static MateMixerClientStreamFlags pulse_client_stream_get_flags (MateMixerClientStream *client);
-static MateMixerClientStreamRole pulse_client_stream_get_role (MateMixerClientStream *client);
-
-static MateMixerStream * pulse_client_stream_get_parent (MateMixerClientStream *client);
-static gboolean pulse_client_stream_set_parent (MateMixerClientStream *client,
- MateMixerStream *parent);
-
-static gboolean pulse_client_stream_remove (MateMixerClientStream *client);
-
-static const gchar * pulse_client_stream_get_app_name (MateMixerClientStream *client);
-static const gchar * pulse_client_stream_get_app_id (MateMixerClientStream *client);
-static const gchar * pulse_client_stream_get_app_version (MateMixerClientStream *client);
-static const gchar * pulse_client_stream_get_app_icon (MateMixerClientStream *client);
-
-static void
-mate_mixer_client_stream_interface_init (MateMixerClientStreamInterface *iface)
-{
- iface->get_flags = pulse_client_stream_get_flags;
- iface->get_role = pulse_client_stream_get_role;
- iface->get_parent = pulse_client_stream_get_parent;
- iface->set_parent = pulse_client_stream_set_parent;
- iface->remove = pulse_client_stream_remove;
- iface->get_app_name = pulse_client_stream_get_app_name;
- iface->get_app_id = pulse_client_stream_get_app_id;
- iface->get_app_version = pulse_client_stream_get_app_version;
- iface->get_app_icon = pulse_client_stream_get_app_icon;
-}
-
-static void
-pulse_client_stream_class_init (PulseClientStreamClass *klass)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (klass);
- object_class->dispose = pulse_client_stream_dispose;
- object_class->finalize = pulse_client_stream_finalize;
- object_class->get_property = pulse_client_stream_get_property;
-
- signals[REMOVED] =
- g_signal_new ("removed",
- G_TYPE_FROM_CLASS (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (PulseClientStreamClass, removed),
- NULL,
- NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0,
- G_TYPE_NONE);
-
- g_object_class_override_property (object_class, PROP_CLIENT_FLAGS, "client-flags");
- g_object_class_override_property (object_class, PROP_ROLE, "role");
- g_object_class_override_property (object_class, PROP_PARENT, "parent");
- g_object_class_override_property (object_class, PROP_APP_NAME, "app-name");
- g_object_class_override_property (object_class, PROP_APP_ID, "app-id");
- g_object_class_override_property (object_class, PROP_APP_VERSION, "app-version");
- g_object_class_override_property (object_class, PROP_APP_ICON, "app-icon");
-
- g_type_class_add_private (object_class, sizeof (PulseClientStreamPrivate));
-}
-
-static void
-pulse_client_stream_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec)
-{
- PulseClientStream *client;
-
- client = PULSE_CLIENT_STREAM (object);
-
- switch (param_id) {
- case PROP_CLIENT_FLAGS:
- g_value_set_flags (value, client->priv->flags);
- break;
- case PROP_ROLE:
- g_value_set_enum (value, client->priv->role);
- break;
- case PROP_PARENT:
- g_value_set_object (value, client->priv->parent);
- break;
- case PROP_APP_NAME:
- g_value_set_string (value, client->priv->app_name);
- break;
- case PROP_APP_ID:
- g_value_set_string (value, client->priv->app_id);
- break;
- case PROP_APP_VERSION:
- g_value_set_string (value, client->priv->app_version);
- break;
- case PROP_APP_ICON:
- g_value_set_string (value, client->priv->app_icon);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
- break;
- }
-}
-
-static void
-pulse_client_stream_init (PulseClientStream *client)
-{
- client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client,
- PULSE_TYPE_CLIENT_STREAM,
- PulseClientStreamPrivate);
-}
-
-static void
-pulse_client_stream_dispose (GObject *object)
-{
- PulseClientStream *client;
-
- client = PULSE_CLIENT_STREAM (object);
-
- g_clear_object (&client->priv->parent);
-
- G_OBJECT_CLASS (pulse_client_stream_parent_class)->dispose (object);
-}
-
-static void
-pulse_client_stream_finalize (GObject *object)
-{
- PulseClientStream *client;
-
- client = PULSE_CLIENT_STREAM (object);
-
- g_free (client->priv->app_name);
- g_free (client->priv->app_id);
- g_free (client->priv->app_version);
- g_free (client->priv->app_icon);
-
- G_OBJECT_CLASS (pulse_client_stream_parent_class)->finalize (object);
-}
-
-gboolean
-pulse_client_stream_update_flags (PulseClientStream *pclient,
- MateMixerClientStreamFlags flags)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
-
- if (pclient->priv->flags != flags) {
- pclient->priv->flags = flags;
-
- g_object_notify (G_OBJECT (pclient), "client-flags");
- }
- return TRUE;
-}
-
-gboolean
-pulse_client_stream_update_parent (PulseClientStream *pclient, MateMixerStream *parent)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
-
- if (pclient->priv->parent != parent) {
- g_clear_object (&pclient->priv->parent);
-
- if (G_LIKELY (parent != NULL))
- pclient->priv->parent = g_object_ref (parent);
-
- g_object_notify (G_OBJECT (pclient), "parent");
- }
- return TRUE;
-}
-
-gboolean
-pulse_client_stream_update_role (PulseClientStream *pclient,
- MateMixerClientStreamRole role)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
-
- if (pclient->priv->role != role) {
- pclient->priv->role = role;
-
- g_object_notify (G_OBJECT (pclient), "role");
- }
- return TRUE;
-}
-
-gboolean
-pulse_client_stream_update_app_name (PulseClientStream *pclient, const gchar *app_name)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
-
- if (g_strcmp0 (pclient->priv->app_name, app_name) != 0) {
- g_free (pclient->priv->app_name);
- pclient->priv->app_name = g_strdup (app_name);
-
- g_object_notify (G_OBJECT (pclient), "app-name");
- }
- return TRUE;
-}
-
-gboolean
-pulse_client_stream_update_app_id (PulseClientStream *pclient, const gchar *app_id)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
-
- if (g_strcmp0 (pclient->priv->app_id, app_id) != 0) {
- g_free (pclient->priv->app_id);
- pclient->priv->app_id = g_strdup (app_id);
-
- g_object_notify (G_OBJECT (pclient), "app-id");
- }
- return TRUE;
-}
-
-gboolean
-pulse_client_stream_update_app_version (PulseClientStream *pclient, const gchar *app_version)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
-
- if (g_strcmp0 (pclient->priv->app_version, app_version) != 0) {
- g_free (pclient->priv->app_version);
- pclient->priv->app_version = g_strdup (app_version);
-
- g_object_notify (G_OBJECT (pclient), "app-version");
- }
- return TRUE;
-}
-
-gboolean
-pulse_client_stream_update_app_icon (PulseClientStream *pclient, const gchar *app_icon)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (pclient), FALSE);
-
- if (g_strcmp0 (pclient->priv->app_icon, app_icon) != 0) {
- g_free (pclient->priv->app_icon);
- pclient->priv->app_icon = g_strdup (app_icon);
-
- g_object_notify (G_OBJECT (pclient), "app-icon");
- }
- return TRUE;
-}
-
-static MateMixerClientStreamFlags
-pulse_client_stream_get_flags (MateMixerClientStream *client)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_NO_FLAGS);
-
- return PULSE_CLIENT_STREAM (client)->priv->flags;
-}
-
-static MateMixerClientStreamRole
-pulse_client_stream_get_role (MateMixerClientStream *client)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), MATE_MIXER_CLIENT_STREAM_ROLE_NONE);
-
- return PULSE_CLIENT_STREAM (client)->priv->role;
-}
-
-static MateMixerStream *
-pulse_client_stream_get_parent (MateMixerClientStream *client)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
-
- return PULSE_CLIENT_STREAM (client)->priv->parent;
-}
-
-static gboolean
-pulse_client_stream_set_parent (MateMixerClientStream *client, MateMixerStream *parent)
-{
- MateMixerStreamFlags flags;
- PulseClientStream *pclient;
- PulseClientStreamClass *klass;
-
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
- g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE);
-
- pclient = PULSE_CLIENT_STREAM (client);
- klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient);
-
- if (pclient->priv->parent == parent)
- return TRUE;
-
- flags = mate_mixer_stream_get_flags (MATE_MIXER_STREAM (pclient));
-
- /* Validate the parent stream */
- if (flags & MATE_MIXER_STREAM_INPUT && !PULSE_IS_SOURCE (parent)) {
- g_warning ("Could not change stream parent to %s: not a parent input stream",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)));
- return FALSE;
- } else if (flags & MATE_MIXER_STREAM_OUTPUT && !PULSE_IS_SINK (parent)) {
- g_warning ("Could not change stream parent to %s: not a parent output stream",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent)));
- return FALSE;
- }
-
- /* Set the parent */
- if (klass->set_parent (pclient, PULSE_STREAM (parent)) == FALSE)
- return FALSE;
-
- if (pclient->priv->parent != NULL)
- g_object_unref (pclient->priv->parent);
-
- /* It is allowed for the parent to be NULL when the instance is created, but
- * changing the parent requires a valid parent stream */
- pclient->priv->parent = g_object_ref (parent);
-
- g_object_notify (G_OBJECT (client), "parent");
- return TRUE;
-}
-
-static gboolean
-pulse_client_stream_remove (MateMixerClientStream *client)
-{
- PulseClientStream *pclient;
- PulseClientStreamClass *klass;
-
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), FALSE);
-
- pclient = PULSE_CLIENT_STREAM (client);
- klass = PULSE_CLIENT_STREAM_GET_CLASS (pclient);
-
- if (klass->remove (pclient) == FALSE)
- return FALSE;
-
- // XXX handle this in the backend
- g_signal_emit (G_OBJECT (client),
- signals[REMOVED],
- 0);
-
- return TRUE;
-}
-
-static const gchar *
-pulse_client_stream_get_app_name (MateMixerClientStream *client)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
-
- return PULSE_CLIENT_STREAM (client)->priv->app_name;
-}
-
-static const gchar *
-pulse_client_stream_get_app_id (MateMixerClientStream *client)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
-
- return PULSE_CLIENT_STREAM (client)->priv->app_id;
-}
-
-static const gchar *
-pulse_client_stream_get_app_version (MateMixerClientStream *client)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
-
- return PULSE_CLIENT_STREAM (client)->priv->app_version;
-}
-
-static const gchar *
-pulse_client_stream_get_app_icon (MateMixerClientStream *client)
-{
- g_return_val_if_fail (PULSE_IS_CLIENT_STREAM (client), NULL);
-
- return PULSE_CLIENT_STREAM (client)->priv->app_icon;
-}
diff --git a/backends/pulse/pulse-client-stream.h b/backends/pulse/pulse-client-stream.h
deleted file mode 100644
index fe24dc3..0000000
--- a/backends/pulse/pulse-client-stream.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2014 Michal Ratajsky <[email protected]>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the licence, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef PULSE_CLIENT_STREAM_H
-#define PULSE_CLIENT_STREAM_H
-
-#include <glib.h>
-#include <glib-object.h>
-
-#include <libmatemixer/matemixer-client-stream.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
-
-#include "pulse-stream.h"
-
-G_BEGIN_DECLS
-
-#define PULSE_TYPE_CLIENT_STREAM \
- (pulse_client_stream_get_type ())
-#define PULSE_CLIENT_STREAM(o) \
- (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStream))
-#define PULSE_IS_CLIENT_STREAM(o) \
- (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_CLIENT_STREAM))
-#define PULSE_CLIENT_STREAM_CLASS(k) \
- (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass))
-#define PULSE_IS_CLIENT_STREAM_CLASS(k) \
- (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_CLIENT_STREAM))
-#define PULSE_CLIENT_STREAM_GET_CLASS(o) \
- (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CLIENT_STREAM, PulseClientStreamClass))
-
-typedef struct _PulseClientStream PulseClientStream;
-typedef struct _PulseClientStreamClass PulseClientStreamClass;
-typedef struct _PulseClientStreamPrivate PulseClientStreamPrivate;
-
-struct _PulseClientStream
-{
- PulseStream parent;
-
- /*< private >*/
- PulseClientStreamPrivate *priv;
-};
-
-struct _PulseClientStreamClass
-{
- PulseStreamClass parent_class;
-
- /*< private >*/
- /* Virtual table */
- gboolean (*set_parent) (PulseClientStream *pclient,
- PulseStream *pstream);
-
- gboolean (*remove) (PulseClientStream *pclient);
-
- /* Signals */
- void (*removed) (PulseClientStream *pclient);
-};
-
-GType pulse_client_stream_get_type (void) G_GNUC_CONST;
-
-gboolean pulse_client_stream_update_flags (PulseClientStream *pclient,
- MateMixerClientStreamFlags flags);
-
-gboolean pulse_client_stream_update_role (PulseClientStream *pclient,
- MateMixerClientStreamRole role);
-
-gboolean pulse_client_stream_update_parent (PulseClientStream *pclient,
- MateMixerStream *parent);
-
-gboolean pulse_client_stream_update_app_name (PulseClientStream *pclient,
- const gchar *app_name);
-gboolean pulse_client_stream_update_app_id (PulseClientStream *pclient,
- const gchar *app_id);
-gboolean pulse_client_stream_update_app_version (PulseClientStream *pclient,
- const gchar *app_version);
-gboolean pulse_client_stream_update_app_icon (PulseClientStream *pclient,
- const gchar *app_icon);
-
-G_END_DECLS
-
-#endif /* PULSE_CLIENT_STREAM_H */
diff --git a/backends/pulse/pulse-connection.c b/backends/pulse/pulse-connection.c
index cc39caf..7344d2e 100644
--- a/backends/pulse/pulse-connection.c
+++ b/backends/pulse/pulse-connection.c
@@ -31,14 +31,14 @@
struct _PulseConnectionPrivate
{
- gchar *server;
- guint outstanding;
- pa_context *context;
- pa_proplist *proplist;
- pa_glib_mainloop *mainloop;
- gboolean ext_streams_loading;
- gboolean ext_streams_dirty;
- PulseConnectionState state;
+ gchar *server;
+ guint outstanding;
+ pa_context *context;
+ pa_proplist *proplist;
+ pa_glib_mainloop *mainloop;
+ gboolean ext_streams_loading;
+ gboolean ext_streams_dirty;
+ PulseConnectionState state;
};
enum {
@@ -417,7 +417,7 @@ pulse_connection_new (const gchar *app_name,
PulseConnection *connection;
mainloop = pa_glib_mainloop_new (g_main_context_get_thread_default ());
- if (G_UNLIKELY (mainloop == NULL)) {
+ if G_UNLIKELY (mainloop == NULL) {
g_warning ("Failed to create PulseAudio main loop");
return NULL;
}
@@ -468,7 +468,7 @@ pulse_connection_connect (PulseConnection *connection, gboolean wait_for_daemon)
context = pa_context_new_with_proplist (mainloop,
NULL,
connection->priv->proplist);
- if (G_UNLIKELY (context == NULL)) {
+ if G_UNLIKELY (context == NULL) {
g_warning ("Failed to create PulseAudio context");
return FALSE;
}
@@ -774,7 +774,6 @@ pulse_connection_create_monitor (PulseConnection *connection,
return pulse_monitor_new (connection->priv->context,
connection->priv->proplist,
- NULL,
index_source,
index_sink_input);
}
@@ -1227,7 +1226,7 @@ load_lists (PulseConnection *connection)
GSList *ops = NULL;
pa_operation *op;
- if (G_UNLIKELY (connection->priv->outstanding > 0)) {
+ if G_UNLIKELY (connection->priv->outstanding > 0) {
g_warn_if_reached ();
return FALSE;
}
@@ -1235,7 +1234,7 @@ load_lists (PulseConnection *connection)
op = pa_context_get_card_info_list (connection->priv->context,
pulse_card_info_cb,
connection);
- if (G_UNLIKELY (op == NULL))
+ if G_UNLIKELY (op == NULL)
goto error;
ops = g_slist_prepend (ops, op);
@@ -1243,7 +1242,7 @@ load_lists (PulseConnection *connection)
op = pa_context_get_sink_info_list (connection->priv->context,
pulse_sink_info_cb,
connection);
- if (G_UNLIKELY (op == NULL))
+ if G_UNLIKELY (op == NULL)
goto error;
ops = g_slist_prepend (ops, op);
@@ -1251,7 +1250,7 @@ load_lists (PulseConnection *connection)
op = pa_context_get_sink_input_info_list (connection->priv->context,
pulse_sink_input_info_cb,
connection);
- if (G_UNLIKELY (op == NULL))
+ if G_UNLIKELY (op == NULL)
goto error;
ops = g_slist_prepend (ops, op);
@@ -1259,7 +1258,7 @@ load_lists (PulseConnection *connection)
op = pa_context_get_source_info_list (connection->priv->context,
pulse_source_info_cb,
connection);
- if (G_UNLIKELY (op == NULL))
+ if G_UNLIKELY (op == NULL)
goto error;
ops = g_slist_prepend (ops, op);
@@ -1267,7 +1266,7 @@ load_lists (PulseConnection *connection)
op = pa_context_get_source_output_info_list (connection->priv->context,
pulse_source_output_info_cb,
connection);
- if (G_UNLIKELY (op == NULL))
+ if G_UNLIKELY (op == NULL)
goto error;
ops = g_slist_prepend (ops, op);
@@ -1303,7 +1302,7 @@ load_list_finished (PulseConnection *connection)
* as the final step in the connection process */
connection->priv->outstanding--;
- if (G_UNLIKELY (connection->priv->outstanding < 0)) {
+ if G_UNLIKELY (connection->priv->outstanding < 0) {
g_warn_if_reached ();
connection->priv->outstanding = 0;
}
@@ -1311,7 +1310,7 @@ load_list_finished (PulseConnection *connection)
if (connection->priv->outstanding == 0) {
gboolean ret = pulse_connection_load_server_info (connection);
- if (G_UNLIKELY (ret == FALSE)) {
+ if G_UNLIKELY (ret == FALSE) {
pulse_connection_disconnect (connection);
return FALSE;
}
@@ -1640,7 +1639,7 @@ change_state (PulseConnection *connection, PulseConnectionState state)
static gboolean
process_pulse_operation (PulseConnection *connection, pa_operation *op)
{
- if (G_UNLIKELY (op == NULL)) {
+ if G_UNLIKELY (op == NULL) {
g_warning ("PulseAudio operation failed: %s",
pa_strerror (pa_context_errno (connection->priv->context)));
return FALSE;
diff --git a/backends/pulse/pulse-connection.h b/backends/pulse/pulse-connection.h
index b9119fd..7dc0bfb 100644
--- a/backends/pulse/pulse-connection.h
+++ b/backends/pulse/pulse-connection.h
@@ -25,7 +25,7 @@
#include <pulse/ext-stream-restore.h>
#include "pulse-enums.h"
-#include "pulse-monitor.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
@@ -42,7 +42,6 @@ G_BEGIN_DECLS
#define PULSE_CONNECTION_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_CONNECTION, PulseConnectionClass))
-typedef struct _PulseConnection PulseConnection;
typedef struct _PulseConnectionClass PulseConnectionClass;
typedef struct _PulseConnectionPrivate PulseConnectionPrivate;
@@ -59,7 +58,6 @@ struct _PulseConnectionClass
GObjectClass parent_class;
/*< private >*/
- /* Signals */
void (*server_info) (PulseConnection *connection,
const pa_server_info *info);
@@ -201,7 +199,6 @@ gboolean pulse_connection_kill_source_output (PulseConnection
gboolean pulse_connection_write_ext_stream (PulseConnection *connection,
const pa_ext_stream_restore_info *info);
-
gboolean pulse_connection_delete_ext_stream (PulseConnection *connection,
const gchar *name);
diff --git a/backends/pulse/pulse-device-profile.c b/backends/pulse/pulse-device-profile.c
new file mode 100644
index 0000000..5487841
--- /dev/null
+++ b/backends/pulse/pulse-device-profile.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-device.h"
+#include "pulse-device-profile.h"
+
+struct _PulseDeviceProfilePrivate
+{
+ guint priority;
+};
+
+static void pulse_device_profile_class_init (PulseDeviceProfileClass *klass);
+static void pulse_device_profile_init (PulseDeviceProfile *profile);
+
+G_DEFINE_TYPE (PulseDeviceProfile, pulse_device_profile, MATE_MIXER_TYPE_SWITCH_OPTION)
+
+static void
+pulse_device_profile_class_init (PulseDeviceProfileClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (PulseDeviceProfilePrivate));
+}
+
+static void
+pulse_device_profile_init (PulseDeviceProfile *profile)
+{
+ profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (profile,
+ PULSE_TYPE_DEVICE_PROFILE,
+ PulseDeviceProfilePrivate);
+}
+
+PulseDeviceProfile *
+pulse_device_profile_new (const gchar *name,
+ const gchar *label,
+ guint priority)
+{
+ PulseDeviceProfile *profile;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+
+ profile = g_object_new (PULSE_TYPE_DEVICE_PROFILE,
+ "name", name,
+ "label", label,
+ NULL);
+
+ profile->priv->priority = priority;
+ return profile;
+}
+
+const gchar *
+pulse_device_profile_get_name (PulseDeviceProfile *profile)
+{
+ g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (profile), NULL);
+
+ return mate_mixer_switch_option_get_name (MATE_MIXER_SWITCH_OPTION (profile));
+}
+
+guint
+pulse_device_profile_get_priority (PulseDeviceProfile *profile)
+{
+ g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (profile), 0);
+
+ return profile->priv->priority;
+}
diff --git a/backends/pulse/pulse-device-profile.h b/backends/pulse/pulse-device-profile.h
new file mode 100644
index 0000000..0a9c3f4
--- /dev/null
+++ b/backends/pulse/pulse-device-profile.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_DEVICE_PROFILE_H
+#define PULSE_DEVICE_PROFILE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_DEVICE_PROFILE \
+ (pulse_device_profile_get_type ())
+#define PULSE_DEVICE_PROFILE(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE_PROFILE, PulseDeviceProfile))
+#define PULSE_IS_DEVICE_PROFILE(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE_PROFILE))
+#define PULSE_DEVICE_PROFILE_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE_PROFILE, PulseDeviceProfileClass))
+#define PULSE_IS_DEVICE_PROFILE_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE_PROFILE))
+#define PULSE_DEVICE_PROFILE_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE_PROFILE, PulseDeviceProfileClass))
+
+typedef struct _PulseDeviceProfileClass PulseDeviceProfileClass;
+typedef struct _PulseDeviceProfilePrivate PulseDeviceProfilePrivate;
+
+struct _PulseDeviceProfile
+{
+ MateMixerSwitchOption parent;
+
+ /*< private >*/
+ PulseDeviceProfilePrivate *priv;
+};
+
+struct _PulseDeviceProfileClass
+{
+ MateMixerSwitchOptionClass parent;
+};
+
+GType pulse_device_profile_get_type (void) G_GNUC_CONST;
+
+PulseDeviceProfile *pulse_device_profile_new (const gchar *name,
+ const gchar *label,
+ guint priority);
+
+const gchar * pulse_device_profile_get_name (PulseDeviceProfile *profile);
+guint pulse_device_profile_get_priority (PulseDeviceProfile *profile);
+
+G_END_DECLS
+
+#endif /* PULSE_DEVICE_PROFILE_H */
diff --git a/backends/pulse/pulse-device-switch.c b/backends/pulse/pulse-device-switch.c
new file mode 100644
index 0000000..7a43d0a
--- /dev/null
+++ b/backends/pulse/pulse-device-switch.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "pulse-connection.h"
+#include "pulse-device.h"
+#include "pulse-device-profile.h"
+#include "pulse-device-switch.h"
+
+struct _PulseDeviceSwitchPrivate
+{
+ GList *profiles;
+ PulseDevice *device;
+};
+
+enum {
+ PROP_0,
+ PROP_DEVICE,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void pulse_device_switch_class_init (PulseDeviceSwitchClass *klass);
+
+static void pulse_device_switch_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_device_switch_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_device_switch_init (PulseDeviceSwitch *swtch);
+static void pulse_device_switch_dispose (GObject *object);
+
+G_DEFINE_TYPE (PulseDeviceSwitch, pulse_device_switch, MATE_MIXER_TYPE_SWITCH)
+
+static gboolean pulse_device_switch_set_active_option (MateMixerSwitch *mms,
+ MateMixerSwitchOption *mmso);
+
+static const GList *pulse_device_switch_list_options (MateMixerSwitch *mms);
+
+static gint compare_profiles (gconstpointer a,
+ gconstpointer b);
+static gint compare_profile_name (gconstpointer a,
+ gconstpointer b);
+
+static void
+pulse_device_switch_class_init (PulseDeviceSwitchClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerSwitchClass *switch_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_device_switch_dispose;
+ object_class->get_property = pulse_device_switch_get_property;
+ object_class->set_property = pulse_device_switch_set_property;
+
+ switch_class = MATE_MIXER_SWITCH_CLASS (klass);
+ switch_class->set_active_option = pulse_device_switch_set_active_option;
+ switch_class->list_options = pulse_device_switch_list_options;
+
+ properties[PROP_DEVICE] =
+ g_param_spec_object ("device",
+ "Device",
+ "PulseAudio device",
+ PULSE_TYPE_DEVICE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (PulseDeviceSwitchPrivate));
+}
+
+static void
+pulse_device_switch_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PulseDeviceSwitch *swtch;
+
+ swtch = PULSE_DEVICE_SWITCH (object);
+
+ switch (param_id) {
+ case PROP_DEVICE:
+ g_value_set_object (value, swtch->priv->device);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_device_switch_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PulseDeviceSwitch *swtch;
+
+ swtch = PULSE_DEVICE_SWITCH (object);
+
+ switch (param_id) {
+ case PROP_DEVICE:
+ /* Construct-only object */
+ swtch->priv->device = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_device_switch_init (PulseDeviceSwitch *swtch)
+{
+ swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch,
+ PULSE_TYPE_DEVICE_SWITCH,
+ PulseDeviceSwitchPrivate);
+}
+
+static void
+pulse_device_switch_dispose (GObject *object)
+{
+ PulseDeviceSwitch *swtch;
+
+ swtch = PULSE_DEVICE_SWITCH (object);
+
+ g_clear_object (&swtch->priv->device);
+
+ G_OBJECT_CLASS (pulse_device_switch_parent_class)->dispose (object);
+}
+
+PulseDeviceSwitch *
+pulse_device_switch_new (const gchar *name, const gchar *label, PulseDevice *device)
+{
+ return g_object_new (PULSE_TYPE_DEVICE_SWITCH,
+ "name", name,
+ "label", label,
+ "role", MATE_MIXER_SWITCH_ROLE_DEVICE_PROFILE,
+ "device", device,
+ NULL);
+}
+
+PulseDevice *
+pulse_device_switch_get_device (PulseDeviceSwitch *swtch)
+{
+ g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (swtch), NULL);
+
+ return swtch->priv->device;
+}
+
+void
+pulse_device_switch_add_profile (PulseDeviceSwitch *swtch, PulseDeviceProfile *profile)
+{
+ g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch));
+ g_return_if_fail (PULSE_IS_DEVICE_PROFILE (profile));
+
+ swtch->priv->profiles = g_list_insert_sorted (swtch->priv->profiles,
+ profile,
+ compare_profiles);
+}
+
+void
+pulse_device_switch_set_active_profile (PulseDeviceSwitch *swtch,
+ PulseDeviceProfile *profile)
+{
+ g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch));
+ g_return_if_fail (PULSE_IS_DEVICE_PROFILE (profile));
+
+ _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch),
+ MATE_MIXER_SWITCH_OPTION (profile));
+}
+
+void
+pulse_device_switch_set_active_profile_by_name (PulseDeviceSwitch *swtch, const gchar *name)
+{
+ GList *item;
+
+ g_return_if_fail (PULSE_IS_DEVICE_SWITCH (swtch));
+ g_return_if_fail (name != NULL);
+
+ item = g_list_find_custom (swtch->priv->profiles, name, compare_profile_name);
+ if G_UNLIKELY (item == NULL) {
+ g_debug ("Invalid device switch profile name %s", name);
+ return;
+ }
+ pulse_device_switch_set_active_profile (swtch, PULSE_DEVICE_PROFILE (item->data));
+}
+
+static gboolean
+pulse_device_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso)
+{
+ PulseDevice *device;
+ const gchar *device_name;
+ const gchar *profile_name;
+
+ g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (mms), FALSE);
+ g_return_val_if_fail (PULSE_IS_DEVICE_PROFILE (mmso), FALSE);
+
+ device = pulse_device_switch_get_device (PULSE_DEVICE_SWITCH (mms));
+ if G_UNLIKELY (device == NULL)
+ return FALSE;
+
+ device_name = mate_mixer_device_get_name (MATE_MIXER_DEVICE (device));
+ profile_name = mate_mixer_switch_option_get_name (mmso);
+
+ return pulse_connection_set_card_profile (pulse_device_get_connection (device),
+ device_name,
+ profile_name);
+}
+
+static const GList *
+pulse_device_switch_list_options (MateMixerSwitch *swtch)
+{
+ g_return_val_if_fail (PULSE_IS_DEVICE_SWITCH (swtch), NULL);
+
+ return PULSE_DEVICE_SWITCH (swtch)->priv->profiles;
+}
+
+static gint
+compare_profiles (gconstpointer a, gconstpointer b)
+{
+ return pulse_device_profile_get_priority (PULSE_DEVICE_PROFILE (b)) -
+ pulse_device_profile_get_priority (PULSE_DEVICE_PROFILE (a));
+}
+
+static gint
+compare_profile_name (gconstpointer a, gconstpointer b)
+{
+ PulseDeviceProfile *profile = PULSE_DEVICE_PROFILE (a);
+ const gchar *name = (const gchar *) b;
+
+ return strcmp (pulse_device_profile_get_name (profile), name);
+}
diff --git a/backends/pulse/pulse-device-switch.h b/backends/pulse/pulse-device-switch.h
new file mode 100644
index 0000000..50f4a68
--- /dev/null
+++ b/backends/pulse/pulse-device-switch.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_DEVICE_SWITCH_H
+#define PULSE_DEVICE_SWITCH_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_DEVICE_SWITCH \
+ (pulse_device_switch_get_type ())
+#define PULSE_DEVICE_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitch))
+#define PULSE_IS_DEVICE_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE_SWITCH))
+#define PULSE_DEVICE_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitchClass))
+#define PULSE_IS_DEVICE_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE_SWITCH))
+#define PULSE_DEVICE_SWITCH_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_DEVICE_SWITCH, PulseDeviceSwitchClass))
+
+typedef struct _PulseDeviceSwitchClass PulseDeviceSwitchClass;
+typedef struct _PulseDeviceSwitchPrivate PulseDeviceSwitchPrivate;
+
+struct _PulseDeviceSwitch
+{
+ MateMixerSwitch parent;
+
+ /*< private >*/
+ PulseDeviceSwitchPrivate *priv;
+};
+
+struct _PulseDeviceSwitchClass
+{
+ MateMixerSwitchClass parent_class;
+};
+
+GType pulse_device_switch_get_type (void) G_GNUC_CONST;
+
+PulseDeviceSwitch *pulse_device_switch_new (const gchar *name,
+ const gchar *label,
+ PulseDevice *device);
+
+PulseDevice * pulse_device_switch_get_device (PulseDeviceSwitch *swtch);
+
+void pulse_device_switch_add_profile (PulseDeviceSwitch *swtch,
+ PulseDeviceProfile *profile);
+
+void pulse_device_switch_set_active_profile (PulseDeviceSwitch *swtch,
+ PulseDeviceProfile *profile);
+
+void pulse_device_switch_set_active_profile_by_name (PulseDeviceSwitch *swtch,
+ const gchar *name);
+
+G_END_DECLS
+
+#endif /* PULSE_DEVICE_SWITCH_H */
diff --git a/backends/pulse/pulse-device.c b/backends/pulse/pulse-device.c
index 96e06c8..5403712 100644
--- a/backends/pulse/pulse-device.c
+++ b/backends/pulse/pulse-device.c
@@ -17,45 +17,40 @@
#include <string.h>
#include <glib.h>
+#include <glib/gi18n.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-device-profile.h>
-#include <libmatemixer/matemixer-device-profile-private.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-port.h>
-#include <libmatemixer/matemixer-port-private.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
#include "pulse-device.h"
+#include "pulse-device-profile.h"
+#include "pulse-device-switch.h"
+#include "pulse-port.h"
+#include "pulse-stream.h"
struct _PulseDevicePrivate
{
- guint32 index;
- gchar *name;
- gchar *description;
- gchar *icon;
- GHashTable *ports;
- GList *ports_list;
- GHashTable *profiles;
- GList *profiles_list;
- PulseConnection *connection;
- MateMixerDeviceProfile *profile;
+ guint32 index;
+ GHashTable *ports;
+ GHashTable *streams;
+ GList *streams_list;
+ GList *switches_list;
+ PulseConnection *connection;
+ PulseDeviceSwitch *pswitch;
};
enum {
PROP_0,
- PROP_NAME,
- PROP_DESCRIPTION,
- PROP_ICON,
- PROP_ACTIVE_PROFILE,
PROP_INDEX,
- PROP_CONNECTION
+ PROP_CONNECTION,
+ N_PROPERTIES
};
-static void mate_mixer_device_interface_init (MateMixerDeviceInterface *iface);
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
static void pulse_device_class_init (PulseDeviceClass *klass);
@@ -72,60 +67,25 @@ static void pulse_device_init (PulseDevice *device);
static void pulse_device_dispose (GObject *object);
static void pulse_device_finalize (GObject *object);
-G_DEFINE_TYPE_WITH_CODE (PulseDevice, pulse_device, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_DEVICE,
- mate_mixer_device_interface_init))
+G_DEFINE_TYPE (PulseDevice, pulse_device, MATE_MIXER_TYPE_DEVICE)
-#if PA_CHECK_VERSION (5, 0, 0)
-typedef pa_card_profile_info2 _pa_card_profile_info;
-#else
-typedef pa_card_profile_info _pa_card_profile_info;
-#endif
-
-static const gchar * pulse_device_get_name (MateMixerDevice *device);
-static const gchar * pulse_device_get_description (MateMixerDevice *device);
-static const gchar * pulse_device_get_icon (MateMixerDevice *device);
+static MateMixerStream *pulse_device_get_stream (MateMixerDevice *mmd,
+ const gchar *name);
-static MateMixerPort * pulse_device_get_port (MateMixerDevice *device,
- const gchar *name);
-static MateMixerDeviceProfile *pulse_device_get_profile (MateMixerDevice *device,
- const gchar *name);
+static const GList * pulse_device_list_streams (MateMixerDevice *mmd);
+static const GList * pulse_device_list_switches (MateMixerDevice *mmd);
-static const GList * pulse_device_list_ports (MateMixerDevice *device);
-static const GList * pulse_device_list_profiles (MateMixerDevice *device);
+static void pulse_device_load (PulseDevice *device,
+ const pa_card_info *info);
-static MateMixerDeviceProfile *pulse_device_get_active_profile (MateMixerDevice *device);
-static gboolean pulse_device_set_active_profile (MateMixerDevice *device,
- MateMixerDeviceProfile *profile);
-
-static void update_port (PulseDevice *device,
- pa_card_port_info *p_info);
-static void update_profile (PulseDevice *device,
- _pa_card_profile_info *p_info);
-
-static gint compare_ports (gconstpointer a,
- gconstpointer b);
-static gint compare_profiles (gconstpointer a,
- gconstpointer b);
-
-static void
-mate_mixer_device_interface_init (MateMixerDeviceInterface *iface)
-{
- iface->get_name = pulse_device_get_name;
- iface->get_description = pulse_device_get_description;
- iface->get_icon = pulse_device_get_icon;
- iface->get_port = pulse_device_get_port;
- iface->get_profile = pulse_device_get_profile;
- iface->list_ports = pulse_device_list_ports;
- iface->list_profiles = pulse_device_list_profiles;
- iface->get_active_profile = pulse_device_get_active_profile;
- iface->set_active_profile = pulse_device_set_active_profile;
-}
+static void free_list_streams (PulseDevice *device);
+static void free_list_switches (PulseDevice *device);
static void
pulse_device_class_init (PulseDeviceClass *klass)
{
- GObjectClass *object_class;
+ GObjectClass *object_class;
+ MateMixerDeviceClass *device_class;
object_class = G_OBJECT_CLASS (klass);
object_class->dispose = pulse_device_dispose;
@@ -133,31 +93,32 @@ pulse_device_class_init (PulseDeviceClass *klass)
object_class->get_property = pulse_device_get_property;
object_class->set_property = pulse_device_set_property;
- g_object_class_install_property (object_class,
- PROP_INDEX,
- g_param_spec_uint ("index",
- "Index",
- "Device index",
- 0,
- G_MAXUINT,
- 0,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
- g_object_class_install_property (object_class,
- PROP_CONNECTION,
- g_param_spec_object ("connection",
- "Connection",
- "PulseAudio connection",
- PULSE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_override_property (object_class, PROP_NAME, "name");
- g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
- g_object_class_override_property (object_class, PROP_ICON, "icon");
- g_object_class_override_property (object_class, PROP_ACTIVE_PROFILE, "active-profile");
+ device_class = MATE_MIXER_DEVICE_CLASS (klass);
+ device_class->get_stream = pulse_device_get_stream;
+ device_class->list_streams = pulse_device_list_streams;
+ device_class->list_switches = pulse_device_list_switches;
+
+ properties[PROP_INDEX] =
+ g_param_spec_uint ("index",
+ "Index",
+ "Index of the device",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CONNECTION] =
+ g_param_spec_object ("connection",
+ "Connection",
+ "PulseAudio connection",
+ PULSE_TYPE_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
g_type_class_add_private (object_class, sizeof (PulseDevicePrivate));
}
@@ -173,18 +134,6 @@ pulse_device_get_property (GObject *object,
device = PULSE_DEVICE (object);
switch (param_id) {
- case PROP_NAME:
- g_value_set_string (value, device->priv->name);
- break;
- case PROP_DESCRIPTION:
- g_value_set_string (value, device->priv->description);
- break;
- case PROP_ICON:
- g_value_set_string (value, device->priv->icon);
- break;
- case PROP_ACTIVE_PROFILE:
- g_value_set_object (value, device->priv->profile);
- break;
case PROP_INDEX:
g_value_set_uint (value, device->priv->index);
break;
@@ -212,7 +161,6 @@ pulse_device_set_property (GObject *object,
device->priv->index = g_value_get_uint (value);
break;
case PROP_CONNECTION:
- /* Construct-only object */
device->priv->connection = g_value_dup_object (value);
break;
default:
@@ -233,10 +181,10 @@ pulse_device_init (PulseDevice *device)
g_free,
g_object_unref);
- device->priv->profiles = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
+ device->priv->streams = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
}
static void
@@ -246,20 +194,14 @@ pulse_device_dispose (GObject *object)
device = PULSE_DEVICE (object);
- if (device->priv->ports_list != NULL) {
- g_list_free_full (device->priv->ports_list, g_object_unref);
- device->priv->ports_list = NULL;
- }
g_hash_table_remove_all (device->priv->ports);
+ g_hash_table_remove_all (device->priv->streams);
- if (device->priv->profiles_list != NULL) {
- g_list_free_full (device->priv->profiles_list, g_object_unref);
- device->priv->profiles_list = NULL;
- }
- g_hash_table_remove_all (device->priv->profiles);
-
- g_clear_object (&device->priv->profile);
g_clear_object (&device->priv->connection);
+ g_clear_object (&device->priv->pswitch);
+
+ free_list_streams (device);
+ free_list_switches (device);
G_OBJECT_CLASS (pulse_device_parent_class)->dispose (object);
}
@@ -271,12 +213,8 @@ pulse_device_finalize (GObject *object)
device = PULSE_DEVICE (object);
- g_free (device->priv->name);
- g_free (device->priv->description);
- g_free (device->priv->icon);
-
- g_hash_table_destroy (device->priv->ports);
- g_hash_table_destroy (device->priv->profiles);
+ g_hash_table_unref (device->priv->ports);
+ g_hash_table_unref (device->priv->streams);
G_OBJECT_CLASS (pulse_device_parent_class)->finalize (object);
}
@@ -285,123 +223,86 @@ PulseDevice *
pulse_device_new (PulseConnection *connection, const pa_card_info *info)
{
PulseDevice *device;
+ const gchar *label;
+ const gchar *icon;
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
g_return_val_if_fail (info != NULL, NULL);
+ label = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION);
+ if G_UNLIKELY (label == NULL)
+ label = info->name;
+
+ icon = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME);
+ if G_UNLIKELY (icon == NULL)
+ icon = "audio-card";
+
/* Consider the device index as unchanging parameter */
device = g_object_new (PULSE_TYPE_DEVICE,
- "connection", connection,
"index", info->index,
+ "connection", connection,
+ "name", info->name,
+ "label", label,
+ "icon", icon,
NULL);
- /* Other data may change at any time, so let's make a use of our update function */
+ pulse_device_load (device, info);
pulse_device_update (device, info);
return device;
}
-gboolean
+void
pulse_device_update (PulseDevice *device, const pa_card_info *info)
{
- MateMixerDeviceProfile *profile = NULL;
- const gchar *prop;
- guint32 i;
-
- g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
-
- /* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (device));
-
- /* Name */
- if (g_strcmp0 (device->priv->name, info->name) != 0) {
- g_free (device->priv->name);
- device->priv->name = g_strdup (info->name);
-
- g_object_notify (G_OBJECT (device), "name");
- }
-
- /* Description */
- prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_DESCRIPTION);
-
- if (G_UNLIKELY (prop == NULL))
- prop = info->name;
-
- if (g_strcmp0 (device->priv->description, prop) != 0) {
- g_free (device->priv->description);
- device->priv->description = g_strdup (prop);
-
- g_object_notify (G_OBJECT (device), "description");
- }
-
- /* Icon */
- prop = pa_proplist_gets (info->proplist, PA_PROP_DEVICE_ICON_NAME);
-
- if (G_UNLIKELY (prop == NULL))
- prop = "audio-card";
-
- if (g_strcmp0 (device->priv->icon, prop) != 0) {
- g_free (device->priv->icon);
- device->priv->icon = g_strdup (prop);
-
- g_object_notify (G_OBJECT (device), "icon");
- }
-
-#if PA_CHECK_VERSION (2, 0, 0)
- /* List of ports */
- for (i = 0; i < info->n_ports; i++) {
- update_port (device, info->ports[i]);
- }
-#endif
+ g_return_if_fail (PULSE_IS_DEVICE (device));
+ g_return_if_fail (info != NULL);
- /* List of profiles */
- for (i = 0; i < info->n_profiles; i++) {
#if PA_CHECK_VERSION (5, 0, 0)
- pa_card_profile_info2 *p_info = info->profiles2[i];
-
- /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only
- * differs in the new available flag, we use it not to include profiles
- * which are unavailable */
- if (p_info->available == 0)
- continue;
+ if G_LIKELY (info->active_profile2 != NULL)
+ pulse_device_switch_set_active_profile_by_name (device->priv->pswitch,
+ info->active_profile2->name);
#else
- /* The old profile list is an array of structs, not pointers */
- pa_card_profile_info *p_info = &info->profiles[i];
+ if G_LIKELY (info->active_profile != NULL)
+ pulse_device_switch_set_active_profile_by_name (device->priv->pswitch,
+ info->active_profile->name);
#endif
- update_profile (device, p_info);
- }
-
- /* Figure out whether the currently active profile has changed */
- profile = NULL;
+}
-#if PA_CHECK_VERSION (5, 0, 0)
- if (G_LIKELY (info->active_profile2 != NULL))
- profile = g_hash_table_lookup (device->priv->profiles, info->active_profile2->name);
-#else
- if (G_LIKELY (info->active_profile != NULL))
- profile = g_hash_table_lookup (device->priv->profiles, info->active_profile->name);
-#endif
+void
+pulse_device_add_stream (PulseDevice *device, PulseStream *stream)
+{
+ const gchar *name;
- if (profile != device->priv->profile) {
- g_clear_object (&device->priv->profile);
+ g_return_if_fail (PULSE_IS_DEVICE (device));
+ g_return_if_fail (PULSE_IS_STREAM (stream));
- if (G_LIKELY (profile != NULL))
- device->priv->profile = g_object_ref (profile);
+ name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
- g_object_notify (G_OBJECT (device), "active-profile");
- }
+ free_list_streams (device);
- g_object_thaw_notify (G_OBJECT (device));
- return TRUE;
+ g_hash_table_insert (device->priv->streams, g_strdup (name), stream);
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-added",
+ name);
}
-PulseConnection *
-pulse_device_get_connection (PulseDevice *device)
+void
+pulse_device_remove_stream (PulseDevice *device, PulseStream *stream)
{
- g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
+ const gchar *name;
- return device->priv->connection;
+ g_return_if_fail (PULSE_IS_DEVICE (device));
+ g_return_if_fail (PULSE_IS_STREAM (stream));
+
+ name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (stream));
+
+ free_list_streams (device);
+
+ g_hash_table_remove (device->priv->streams, name);
+ g_signal_emit_by_name (G_OBJECT (device),
+ "stream-removed",
+ name);
}
guint32
@@ -412,222 +313,130 @@ pulse_device_get_index (PulseDevice *device)
return device->priv->index;
}
-static const gchar *
-pulse_device_get_name (MateMixerDevice *device)
-{
- g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
-
- return PULSE_DEVICE (device)->priv->name;
-}
-
-static const gchar *
-pulse_device_get_description (MateMixerDevice *device)
-{
- g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
-
- return PULSE_DEVICE (device)->priv->description;
-}
-
-static const gchar *
-pulse_device_get_icon (MateMixerDevice *device)
+PulseConnection *
+pulse_device_get_connection (PulseDevice *device)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
- return PULSE_DEVICE (device)->priv->icon;
+ return device->priv->connection;
}
-static MateMixerPort *
-pulse_device_get_port (MateMixerDevice *device, const gchar *name)
+PulsePort *
+pulse_device_get_port (PulseDevice *device, const gchar *name)
{
g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
- g_return_val_if_fail (name != NULL, NULL);
- return g_hash_table_lookup (PULSE_DEVICE (device)->priv->ports, name);
+ return g_hash_table_lookup (device->priv->ports, name);
}
-static MateMixerDeviceProfile *
-pulse_device_get_profile (MateMixerDevice *device, const gchar *name)
+static MateMixerStream *
+pulse_device_get_stream (MateMixerDevice *mmd, const gchar *name)
{
- g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
+ g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL);
g_return_val_if_fail (name != NULL, NULL);
- return g_hash_table_lookup (PULSE_DEVICE (device)->priv->profiles, name);
+ return g_hash_table_lookup (PULSE_DEVICE (mmd)->priv->streams, name);
}
static const GList *
-pulse_device_list_ports (MateMixerDevice *device)
+pulse_device_list_streams (MateMixerDevice *mmd)
{
- PulseDevice *pulse;
-
- g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
-
- pulse = PULSE_DEVICE (device);
+ PulseDevice *device;
- if (pulse->priv->ports_list == NULL) {
- GList *list = g_hash_table_get_values (pulse->priv->ports);
+ g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL);
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
+ device = PULSE_DEVICE (mmd);
- pulse->priv->ports_list = g_list_sort (list, compare_ports);
- }
+ if (device->priv->streams_list == NULL) {
+ device->priv->streams_list = g_hash_table_get_values (device->priv->streams);
+ if (device->priv->streams_list != NULL)
+ g_list_foreach (device->priv->streams_list, (GFunc) g_object_ref, NULL);
}
-
- return (const GList *) pulse->priv->ports_list;
+ return device->priv->streams_list;
}
static const GList *
-pulse_device_list_profiles (MateMixerDevice *device)
+pulse_device_list_switches (MateMixerDevice *mmd)
{
- PulseDevice *pulse;
-
- g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
-
- pulse = PULSE_DEVICE (device);
-
- if (pulse->priv->profiles_list == NULL) {
- GList *list = g_hash_table_get_values (pulse->priv->profiles);
-
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- pulse->priv->profiles_list = g_list_sort (list, compare_profiles);
- }
- }
+ g_return_val_if_fail (PULSE_IS_DEVICE (mmd), NULL);
- return (const GList *) pulse->priv->profiles_list;
+ return PULSE_DEVICE (mmd)->priv->switches_list;
}
-static MateMixerDeviceProfile *
-pulse_device_get_active_profile (MateMixerDevice *device)
+static void
+pulse_device_load (PulseDevice *device, const pa_card_info *info)
{
- g_return_val_if_fail (PULSE_IS_DEVICE (device), NULL);
+ guint i;
- return PULSE_DEVICE (device)->priv->profile;
-}
+#if PA_CHECK_VERSION (2, 0, 0)
+ for (i = 0; i < info->n_ports; i++) {
+ PulsePort *port;
+ const gchar *name;
+ const gchar *icon;
-static gboolean
-pulse_device_set_active_profile (MateMixerDevice *device, MateMixerDeviceProfile *profile)
-{
- PulseDevice *pulse;
- const gchar *name;
- gboolean ret;
+ name = info->ports[i]->name;
+ icon = pa_proplist_gets (info->ports[i]->proplist, "device.icon_name");
- g_return_val_if_fail (PULSE_IS_DEVICE (device), FALSE);
- g_return_val_if_fail (MATE_MIXER_IS_DEVICE_PROFILE (profile), FALSE);
+ port = pulse_port_new (name,
+ info->ports[i]->description,
+ icon,
+ info->ports[i]->priority);
- pulse = PULSE_DEVICE (device);
+ g_hash_table_insert (device->priv->ports, g_strdup (name), port);
+ }
+#endif
- name = mate_mixer_device_profile_get_name (profile);
+ /* Create the device profile switch */
+ if (info->n_profiles > 0) {
+ device->priv->pswitch = pulse_device_switch_new ("profile",
+ _("Profile"),
+ device);
- /* Make sure the profile belongs to the device */
- if (g_hash_table_lookup (pulse->priv->profiles, name) == NULL) {
- g_warning ("Profile %s does not belong to device %s", name, pulse->priv->name);
- return FALSE;
+ device->priv->switches_list = g_list_prepend (NULL, g_object_ref (device->priv->pswitch));
}
- ret = pulse_connection_set_card_profile (pulse->priv->connection,
- pulse->priv->name,
- name);
- if (ret == TRUE) {
- if (pulse->priv->profile != NULL)
- g_object_unref (pulse->priv->profile);
+ for (i = 0; i < info->n_profiles; i++) {
+ PulseDeviceProfile *profile;
+
+#if PA_CHECK_VERSION (5, 0, 0)
+ pa_card_profile_info2 *p_info = info->profiles2[i];
+
+ /* PulseAudio 5.0 includes a new pa_card_profile_info2 which only
+ * differs in the new available flag, we use it not to include profiles
+ * which are unavailable */
+ if (p_info->available == 0)
+ continue;
+#else
+ /* The old profile list is an array of structs, not pointers */
+ pa_card_profile_info *p_info = &info->profiles[i];
+#endif
- pulse->priv->profile = g_object_ref (profile);
+ profile = pulse_device_profile_new (p_info->name,
+ p_info->description,
+ p_info->priority);
- g_object_notify (G_OBJECT (device), "active-profile");
+ pulse_device_switch_add_profile (device->priv->pswitch, profile);
}
- return ret;
}
static void
-update_port (PulseDevice *device, pa_card_port_info *p_info)
+free_list_streams (PulseDevice *device)
{
- MateMixerPort *port;
- MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
- const gchar *icon;
-
- icon = pa_proplist_gets (p_info->proplist, "device.icon_name");
-
- if (p_info->available == PA_PORT_AVAILABLE_YES)
- flags |= MATE_MIXER_PORT_AVAILABLE;
-
- if (p_info->direction & PA_DIRECTION_INPUT)
- flags |= MATE_MIXER_PORT_INPUT;
- if (p_info->direction & PA_DIRECTION_OUTPUT)
- flags |= MATE_MIXER_PORT_OUTPUT;
-
- port = g_hash_table_lookup (device->priv->ports, p_info->name);
-
- if (port != NULL) {
- /* Update existing port */
- _mate_mixer_port_update_description (port, p_info->description);
- _mate_mixer_port_update_icon (port, icon);
- _mate_mixer_port_update_priority (port, p_info->priority);
- _mate_mixer_port_update_flags (port, flags);
- } else {
- /* Add previously unknown port to the hash table */
- port = _mate_mixer_port_new (p_info->name,
- p_info->description,
- icon,
- p_info->priority,
- flags);
-
- g_hash_table_insert (device->priv->ports, g_strdup (p_info->name), port);
- }
+ if (device->priv->streams_list == NULL)
+ return;
+
+ g_list_free_full (device->priv->streams_list, g_object_unref);
+
+ device->priv->streams_list = NULL;
}
static void
-update_profile (PulseDevice *device, _pa_card_profile_info *p_info)
+free_list_switches (PulseDevice *device)
{
- MateMixerDeviceProfile *profile;
-
- profile = g_hash_table_lookup (device->priv->profiles, p_info->name);
-
- if (profile != NULL) {
- /* Update existing profile */
- _mate_mixer_device_profile_update_description (profile, p_info->description);
- _mate_mixer_device_profile_update_priority (profile, p_info->priority);
- _mate_mixer_device_profile_update_num_input_streams (profile, p_info->n_sources);
- _mate_mixer_device_profile_update_num_output_streams (profile, p_info->n_sinks);
- } else {
- /* Add previously unknown profile to the hash table */
- profile = _mate_mixer_device_profile_new (p_info->name,
- p_info->description,
- p_info->priority,
- p_info->n_sources,
- p_info->n_sinks);
-
- g_hash_table_insert (device->priv->profiles, g_strdup (p_info->name), profile);
- }
-}
+ if (device->priv->switches_list == NULL)
+ return;
-static gint
-compare_ports (gconstpointer a, gconstpointer b)
-{
- MateMixerPort *p1 = MATE_MIXER_PORT (a);
- MateMixerPort *p2 = MATE_MIXER_PORT (b);
-
- gint ret = (gint) (mate_mixer_port_get_priority (p2) -
- mate_mixer_port_get_priority (p1));
- if (ret != 0)
- return ret;
- else
- return strcmp (mate_mixer_port_get_name (p1),
- mate_mixer_port_get_name (p2));
-}
+ g_list_free_full (device->priv->switches_list, g_object_unref);
-static gint
-compare_profiles (gconstpointer a, gconstpointer b)
-{
- MateMixerDeviceProfile *p1 = MATE_MIXER_DEVICE_PROFILE (a);
- MateMixerDeviceProfile *p2 = MATE_MIXER_DEVICE_PROFILE (b);
-
- gint ret = (gint) (mate_mixer_device_profile_get_priority (p2) -
- mate_mixer_device_profile_get_priority (p1));
- if (ret != 0)
- return ret;
- else
- return strcmp (mate_mixer_device_profile_get_name (p1),
- mate_mixer_device_profile_get_name (p2));
+ device->priv->switches_list = NULL;
}
diff --git a/backends/pulse/pulse-device.h b/backends/pulse/pulse-device.h
index 94c331f..863330f 100644
--- a/backends/pulse/pulse-device.h
+++ b/backends/pulse/pulse-device.h
@@ -20,33 +20,33 @@
#include <glib.h>
#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
#include <pulse/pulseaudio.h>
-#include "pulse-connection.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
-#define PULSE_TYPE_DEVICE \
+#define PULSE_TYPE_DEVICE \
(pulse_device_get_type ())
-#define PULSE_DEVICE(o) \
+#define PULSE_DEVICE(o) \
(G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_DEVICE, PulseDevice))
-#define PULSE_IS_DEVICE(o) \
+#define PULSE_IS_DEVICE(o) \
(G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_DEVICE))
-#define PULSE_DEVICE_CLASS(k) \
+#define PULSE_DEVICE_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_DEVICE, PulseDeviceClass))
-#define PULSE_IS_DEVICE_CLASS(k) \
+#define PULSE_IS_DEVICE_CLASS(k) \
(G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_DEVICE))
-#define PULSE_DEVICE_GET_CLASS(o) \
+#define PULSE_DEVICE_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_DEVICE, PulseDeviceClass))
-typedef struct _PulseDevice PulseDevice;
typedef struct _PulseDeviceClass PulseDeviceClass;
typedef struct _PulseDevicePrivate PulseDevicePrivate;
struct _PulseDevice
{
- GObject parent;
+ MateMixerDevice parent;
/*< private >*/
PulseDevicePrivate *priv;
@@ -54,20 +54,29 @@ struct _PulseDevice
struct _PulseDeviceClass
{
- GObjectClass parent;
+ MateMixerDeviceClass parent;
};
GType pulse_device_get_type (void) G_GNUC_CONST;
-PulseDevice *pulse_device_new (PulseConnection *connection,
+PulseDevice * pulse_device_new (PulseConnection *connection,
const pa_card_info *info);
-gboolean pulse_device_update (PulseDevice *device,
+void pulse_device_update (PulseDevice *device,
const pa_card_info *info);
+void pulse_device_add_stream (PulseDevice *device,
+ PulseStream *stream);
+
+void pulse_device_remove_stream (PulseDevice *device,
+ PulseStream *stream);
+
guint32 pulse_device_get_index (PulseDevice *device);
PulseConnection *pulse_device_get_connection (PulseDevice *device);
+PulsePort * pulse_device_get_port (PulseDevice *device,
+ const gchar *name);
+
G_END_DECLS
#endif /* PULSE_DEVICE_H */
diff --git a/backends/pulse/pulse-ext-stream.c b/backends/pulse/pulse-ext-stream.c
index b00e967..3e7490a 100644
--- a/backends/pulse/pulse-ext-stream.c
+++ b/backends/pulse/pulse-ext-stream.c
@@ -19,252 +19,326 @@
#include <glib.h>
#include <glib-object.h>
-#include <libmatemixer/matemixer-client-stream.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include <pulse/ext-stream-restore.h>
#include "pulse-connection.h"
-#include "pulse-client-stream.h"
#include "pulse-ext-stream.h"
#include "pulse-helpers.h"
#include "pulse-stream.h"
+#include "pulse-stream-control.h"
-static void pulse_ext_stream_class_init (PulseExtStreamClass *klass);
-static void pulse_ext_stream_init (PulseExtStream *ext);
+struct _PulseExtStreamPrivate
+{
+ MateMixerAppInfo *app_info;
+ MateMixerDirection direction;
+};
-G_DEFINE_TYPE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_CLIENT_STREAM);
+enum {
+ PROP_0,
+ PROP_DIRECTION
+};
-static void pulse_ext_stream_reload (PulseStream *pstream);
+static void mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface);
-static gboolean pulse_ext_stream_set_mute (PulseStream *pstream,
- gboolean mute);
-static gboolean pulse_ext_stream_set_volume (PulseStream *pstream,
- pa_cvolume *cvolume);
-static gboolean pulse_ext_stream_set_parent (PulseClientStream *pclient,
- PulseStream *parent);
-static gboolean pulse_ext_stream_remove (PulseClientStream *pclient);
+static void pulse_ext_stream_class_init (PulseExtStreamClass *klass);
-static void
-pulse_ext_stream_class_init (PulseExtStreamClass *klass)
-{
- PulseStreamClass *stream_class;
- PulseClientStreamClass *client_class;
+static void pulse_ext_stream_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_ext_stream_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
- stream_class = PULSE_STREAM_CLASS (klass);
+static void pulse_ext_stream_init (PulseExtStream *ext);
- stream_class->reload = pulse_ext_stream_reload;
- stream_class->set_mute = pulse_ext_stream_set_mute;
- stream_class->set_volume = pulse_ext_stream_set_volume;
+G_DEFINE_TYPE_WITH_CODE (PulseExtStream, pulse_ext_stream, PULSE_TYPE_STREAM_CONTROL,
+ G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STORED_CONTROL,
+ mate_mixer_stored_control_interface_init))
- client_class = PULSE_CLIENT_STREAM_CLASS (klass);
+static MateMixerDirection pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc);
- client_class->set_parent = pulse_ext_stream_set_parent;
- client_class->remove = pulse_ext_stream_remove;
-}
+static MateMixerAppInfo * pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc);
+
+static gboolean pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc,
+ MateMixerStream *mms);
+
+static gboolean pulse_ext_stream_set_mute (PulseStreamControl *control,
+ gboolean mute);
+static gboolean pulse_ext_stream_set_volume (PulseStreamControl *control,
+ pa_cvolume *cvolume);
+
+static void fill_ext_stream_restore_info (PulseStreamControl *control,
+ pa_ext_stream_restore_info *info);
static void
-pulse_ext_stream_init (PulseExtStream *ext)
+mate_mixer_stored_control_interface_init (MateMixerStoredControlInterface *iface)
{
+ iface->get_direction = pulse_ext_stream_get_direction;
}
-PulseStream *
-pulse_ext_stream_new (PulseConnection *connection,
- const pa_ext_stream_restore_info *info,
- PulseStream *parent)
+static void
+pulse_ext_stream_class_init (PulseExtStreamClass *klass)
{
- PulseStream *ext;
+ GObjectClass *object_class;
+ MateMixerStreamControlClass *control_class;
+ PulseStreamControlClass *pulse_class;
- g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
- g_return_val_if_fail (info != NULL, NULL);
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->get_property = pulse_ext_stream_get_property;
+ object_class->set_property = pulse_ext_stream_set_property;
- ext = g_object_new (PULSE_TYPE_EXT_STREAM,
- "connection", connection,
- NULL);
+ control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
+ control_class->get_app_info = pulse_ext_stream_get_app_info;
+ control_class->set_stream = pulse_ext_stream_set_stream;
- /* Consider the stream name as unchanging parameter */
- pulse_stream_update_name (ext, info->name);
+ pulse_class = PULSE_STREAM_CONTROL_CLASS (klass);
+ pulse_class->set_mute = pulse_ext_stream_set_mute;
+ pulse_class->set_volume = pulse_ext_stream_set_volume;
- /* Other data may change at any time, so let's make a use of our update function */
- pulse_ext_stream_update (ext, info, parent);
+ g_object_class_override_property (object_class, PROP_DIRECTION, "direction");
- return ext;
+ g_type_class_add_private (object_class, sizeof (PulseExtStreamPrivate));
}
-gboolean
-pulse_ext_stream_update (PulseStream *pstream,
- const pa_ext_stream_restore_info *info,
- PulseStream *parent)
+static void
+pulse_ext_stream_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- MateMixerClientStreamRole role = MATE_MIXER_CLIENT_STREAM_ROLE_NONE;
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_CLIENT |
- MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_HAS_MUTE |
- MATE_MIXER_STREAM_CAN_SET_VOLUME;
- MateMixerClientStreamFlags client_flags =
- MATE_MIXER_CLIENT_STREAM_CACHED;
+ PulseExtStream *ext;
- PulseClientStream *pclient;
- gchar *suffix;
+ ext = PULSE_EXT_STREAM (object);
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
+ switch (param_id) {
+ case PROP_DIRECTION:
+ g_value_set_enum (value, ext->priv->direction);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
- pclient = PULSE_CLIENT_STREAM (pstream);
+static void
+pulse_ext_stream_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PulseExtStream *ext;
- suffix = strchr (info->name, ':');
- if (suffix != NULL)
- suffix++;
+ ext = PULSE_EXT_STREAM (object);
- /* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (pstream));
+ switch (param_id) {
+ case PROP_DIRECTION:
+ ext->priv->direction = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_ext_stream_init (PulseExtStream *ext)
+{
+ ext->priv = G_TYPE_INSTANCE_GET_PRIVATE (ext,
+ PULSE_TYPE_EXT_STREAM,
+ PulseExtStreamPrivate);
+}
+
+PulseExtStream *
+pulse_ext_stream_new (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent)
+{
+ PulseExtStream *ext;
+ gchar *suffix;
+ MateMixerDirection direction;
+ MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
+ MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE;
+ MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
+ MateMixerAppInfo *app_info;
+
+ MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN;
+
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
if (g_str_has_prefix (info->name, "sink-input"))
- flags |= MATE_MIXER_STREAM_OUTPUT;
+ direction = MATE_MIXER_DIRECTION_OUTPUT;
else if (g_str_has_prefix (info->name, "source-output"))
- flags |= MATE_MIXER_STREAM_INPUT;
+ direction = MATE_MIXER_DIRECTION_INPUT;
else
- g_debug ("Unknown ext-stream %s", info->name);
+ direction = MATE_MIXER_DIRECTION_UNKNOWN;
+
+ app_info = _mate_mixer_app_info_new ();
+
+ suffix = strchr (info->name, ':');
+ if (suffix != NULL)
+ suffix++;
if (strstr (info->name, "-by-media-role:")) {
if (G_LIKELY (suffix != NULL))
- role = pulse_convert_media_role_name (suffix);
+ media_role = pulse_convert_media_role_name (suffix);
}
else if (strstr (info->name, "-by-application-name:")) {
- client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION;
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION;
if (G_LIKELY (suffix != NULL))
- pulse_client_stream_update_app_name (pclient, suffix);
+ _mate_mixer_app_info_set_name (app_info, suffix);
}
else if (strstr (info->name, "-by-application-id:")) {
- client_flags |= MATE_MIXER_CLIENT_STREAM_APPLICATION;
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION;
if (G_LIKELY (suffix != NULL))
- pulse_client_stream_update_app_id (pclient, suffix);
+ _mate_mixer_app_info_set_id (app_info, suffix);
}
- /* Flags needed before volume */
- pulse_stream_update_flags (pstream, flags);
+ ext = g_object_new (PULSE_TYPE_EXT_STREAM,
+ "flags", flags,
+ "role", role,
+ "media-role", media_role,
+ "name", info->name,
+ "connection", connection,
+ "direction", direction,
+ "stream", parent,
+ NULL);
- pulse_stream_update_channel_map (pstream, &info->channel_map);
- pulse_stream_update_volume (pstream, &info->volume, 0);
+ if (role == MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION)
+ ext->priv->app_info = app_info;
+ else
+ _mate_mixer_app_info_free (app_info);
- pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
+ pulse_ext_stream_update (ext, info, parent);
+ return ext;
+}
- pulse_client_stream_update_flags (pclient, client_flags);
- pulse_client_stream_update_role (pclient, role);
+void
+pulse_ext_stream_update (PulseExtStream *ext,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent)
+{
+ g_return_if_fail (PULSE_IS_EXT_STREAM (ext));
+ g_return_if_fail (info != NULL);
- if (parent != NULL)
- pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent));
- else
- pulse_client_stream_update_parent (pclient, NULL);
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (ext));
+
+ _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (ext),
+ info->mute ? TRUE : FALSE);
- g_object_thaw_notify (G_OBJECT (pstream));
- return TRUE;
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (ext),
+ &info->channel_map);
+
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (ext),
+ &info->volume,
+ 0);
+
+ _mate_mixer_stream_control_set_stream (MATE_MIXER_STREAM_CONTROL (ext),
+ MATE_MIXER_STREAM (parent));
+
+ g_object_thaw_notify (G_OBJECT (ext));
}
-static void
-pulse_ext_stream_reload (PulseStream *pstream)
+static MateMixerDirection
+pulse_ext_stream_get_direction (MateMixerStoredControl *mmsc)
{
- g_return_if_fail (PULSE_IS_EXT_STREAM (pstream));
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), MATE_MIXER_DIRECTION_UNKNOWN);
- pulse_connection_load_ext_stream_info (pulse_stream_get_connection (pstream));
+ return PULSE_EXT_STREAM (mmsc)->priv->direction;
}
-static gboolean
-pulse_ext_stream_set_mute (PulseStream *pstream, gboolean mute)
+static MateMixerAppInfo *
+pulse_ext_stream_get_app_info (MateMixerStreamControl *mmsc)
{
- MateMixerStream *parent;
- const pa_channel_map *map;
- const pa_cvolume *cvolume;
- pa_ext_stream_restore_info info;
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), NULL);
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE);
+ return PULSE_EXT_STREAM (mmsc)->priv->app_info;
+}
- info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
- info.mute = mute;
+static gboolean
+pulse_ext_stream_set_stream (MateMixerStreamControl *mmsc, MateMixerStream *mms)
+{
+ pa_ext_stream_restore_info info;
- map = pulse_stream_get_channel_map (pstream);
- info.channel_map = *map;
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (mmsc), FALSE);
+ g_return_val_if_fail (mms == NULL || PULSE_IS_STREAM (mms), FALSE);
- cvolume = pulse_stream_get_cvolume (pstream);
- info.volume = *cvolume;
+ fill_ext_stream_restore_info (PULSE_STREAM_CONTROL (mms), &info);
- parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
- if (parent != NULL)
- info.device = mate_mixer_stream_get_name (parent);
+ if (mms != NULL)
+ info.device = mate_mixer_stream_get_name (mms);
else
info.device = NULL;
- return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info);
+ return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (PULSE_STREAM_CONTROL (mmsc)),
+ &info);
}
static gboolean
-pulse_ext_stream_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
+pulse_ext_stream_set_mute (PulseStreamControl *control, gboolean mute)
{
- MateMixerStream *parent;
- const pa_channel_map *map;
pa_ext_stream_restore_info info;
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (pstream), FALSE);
- g_return_val_if_fail (cvolume != NULL, FALSE);
-
- info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
- info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream));
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE);
- map = pulse_stream_get_channel_map (pstream);
- info.channel_map = *map;
-
- parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
- if (parent != NULL)
- info.device = mate_mixer_stream_get_name (parent);
- else
- info.device = NULL;
+ fill_ext_stream_restore_info (control, &info);
- info.volume = *cvolume;
+ info.mute = mute;
- return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info);
+ return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control),
+ &info);
}
static gboolean
-pulse_ext_stream_set_parent (PulseClientStream *pclient, PulseStream *parent)
+pulse_ext_stream_set_volume (PulseStreamControl *control, pa_cvolume *cvolume)
{
- PulseStream *pstream;
- const pa_channel_map *map;
- const pa_cvolume *cvolume;
pa_ext_stream_restore_info info;
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE);
- g_return_val_if_fail (PULSE_IS_STREAM (parent), FALSE);
-
- pstream = PULSE_STREAM (pclient);
-
- info.name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
- info.mute = mate_mixer_stream_get_mute (MATE_MIXER_STREAM (pstream));
+ g_return_val_if_fail (PULSE_IS_EXT_STREAM (control), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
- map = pulse_stream_get_channel_map (pstream);
- info.channel_map = *map;
+ fill_ext_stream_restore_info (control, &info);
- cvolume = pulse_stream_get_cvolume (pstream);
info.volume = *cvolume;
- info.device = mate_mixer_stream_get_name (MATE_MIXER_STREAM (parent));
-
- return pulse_connection_write_ext_stream (pulse_stream_get_connection (pstream), &info);
+ return pulse_connection_write_ext_stream (pulse_stream_control_get_connection (control),
+ &info);
}
-static gboolean
-pulse_ext_stream_remove (PulseClientStream *pclient)
+static void
+fill_ext_stream_restore_info (PulseStreamControl *control,
+ pa_ext_stream_restore_info *info)
{
- PulseStream *pstream;
- const gchar *name;
+ MateMixerStream *stream;
+ MateMixerStreamControl *mmsc;
+ const pa_channel_map *map;
+ const pa_cvolume *cvolume;
+
+ mmsc = MATE_MIXER_STREAM_CONTROL (control);
- g_return_val_if_fail (PULSE_IS_EXT_STREAM (pclient), FALSE);
+ info->name = mate_mixer_stream_control_get_name (mmsc);
+ info->mute = mate_mixer_stream_control_get_mute (mmsc);
- pstream = PULSE_STREAM (pclient);
- name = mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream));
+ map = pulse_stream_control_get_channel_map (control);
+ info->channel_map = *map;
- return pulse_connection_delete_ext_stream (pulse_stream_get_connection (pstream), name);
+ cvolume = pulse_stream_control_get_cvolume (control);
+ info->volume = *cvolume;
+
+ stream = mate_mixer_stream_control_get_stream (mmsc);
+ if (stream != NULL)
+ info->device = mate_mixer_stream_get_name (stream);
+ else
+ info->device = NULL;
}
diff --git a/backends/pulse/pulse-ext-stream.h b/backends/pulse/pulse-ext-stream.h
index e8dabb6..b667dc7 100644
--- a/backends/pulse/pulse-ext-stream.h
+++ b/backends/pulse/pulse-ext-stream.h
@@ -24,9 +24,8 @@
#include <pulse/pulseaudio.h>
#include <pulse/ext-stream-restore.h>
-#include "pulse-client-stream.h"
-#include "pulse-connection.h"
-#include "pulse-stream.h"
+#include "pulse-stream-control.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
@@ -43,28 +42,31 @@ G_BEGIN_DECLS
#define PULSE_EXT_STREAM_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_EXT_STREAM, PulseExtStreamClass))
-typedef struct _PulseExtStream PulseExtStream;
-typedef struct _PulseExtStreamClass PulseExtStreamClass;
+typedef struct _PulseExtStreamClass PulseExtStreamClass;
+typedef struct _PulseExtStreamPrivate PulseExtStreamPrivate;
struct _PulseExtStream
{
- PulseClientStream parent;
+ PulseStreamControl parent;
+
+ /*< private >*/
+ PulseExtStreamPrivate *priv;
};
struct _PulseExtStreamClass
{
- PulseClientStreamClass parent_class;
+ PulseStreamControlClass parent_class;
};
-GType pulse_ext_stream_get_type (void) G_GNUC_CONST;
+GType pulse_ext_stream_get_type (void) G_GNUC_CONST;
-PulseStream *pulse_ext_stream_new (PulseConnection *connection,
- const pa_ext_stream_restore_info *info,
- PulseStream *parent);
+PulseExtStream *pulse_ext_stream_new (PulseConnection *connection,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent);
-gboolean pulse_ext_stream_update (PulseStream *pstream,
- const pa_ext_stream_restore_info *info,
- PulseStream *parent);
+void pulse_ext_stream_update (PulseExtStream *ext,
+ const pa_ext_stream_restore_info *info,
+ PulseStream *parent);
G_END_DECLS
diff --git a/backends/pulse/pulse-helpers.c b/backends/pulse/pulse-helpers.c
index 577f2c6..73f8cdb 100644
--- a/backends/pulse/pulse-helpers.c
+++ b/backends/pulse/pulse-helpers.c
@@ -29,6 +29,7 @@ typedef struct {
pa_channel_position_t pa_position;
} PositionMap;
+// XXX optimize
static PositionMap const position_map[] = {
{ MATE_MIXER_CHANNEL_UNKNOWN, PA_CHANNEL_POSITION_INVALID },
{ MATE_MIXER_CHANNEL_MONO, PA_CHANNEL_POSITION_MONO },
@@ -76,42 +77,42 @@ pulse_convert_position_to_pulse (MateMixerChannelPosition position)
return PA_CHANNEL_POSITION_INVALID;
}
-MateMixerClientStreamRole
+MateMixerStreamControlMediaRole
pulse_convert_media_role_name (const gchar *name)
{
if (!strcmp (name, "video")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_VIDEO;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_VIDEO;
}
else if (!strcmp (name, "music")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_MUSIC;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_MUSIC;
}
else if (!strcmp (name, "game")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_GAME;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_GAME;
}
else if (!strcmp (name, "event")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_EVENT;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT;
}
else if (!strcmp (name, "phone")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_PHONE;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PHONE;
}
else if (!strcmp (name, "animation")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_ANIMATION;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ANIMATION;
}
else if (!strcmp (name, "production")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_PRODUCTION;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_PRODUCTION;
}
else if (!strcmp (name, "a11y")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_A11Y;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_A11Y;
}
else if (!strcmp (name, "test")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_TEST;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_TEST;
}
else if (!strcmp (name, "abstract")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_ABSTRACT;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_ABSTRACT;
}
else if (!strcmp (name, "filter")) {
- return MATE_MIXER_CLIENT_STREAM_ROLE_FILTER;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_FILTER;
}
- return MATE_MIXER_CLIENT_STREAM_ROLE_NONE;
+ return MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN;
}
diff --git a/backends/pulse/pulse-helpers.h b/backends/pulse/pulse-helpers.h
index 7ccd753..667fc3c 100644
--- a/backends/pulse/pulse-helpers.h
+++ b/backends/pulse/pulse-helpers.h
@@ -19,17 +19,16 @@
#define PULSE_HELPERS_H
#include <glib.h>
-
-#include <libmatemixer/matemixer-enums.h>
+#include <libmatemixer/matemixer.h>
#include <pulse/pulseaudio.h>
G_BEGIN_DECLS
-MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position);
-pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position);
+MateMixerChannelPosition pulse_convert_position_from_pulse (pa_channel_position_t position);
+pa_channel_position_t pulse_convert_position_to_pulse (MateMixerChannelPosition position);
-MateMixerClientStreamRole pulse_convert_media_role_name (const gchar *name);
+MateMixerStreamControlMediaRole pulse_convert_media_role_name (const gchar *name);
G_END_DECLS
diff --git a/backends/pulse/pulse-monitor.c b/backends/pulse/pulse-monitor.c
index 3d5b4a8..915b71b 100644
--- a/backends/pulse/pulse-monitor.c
+++ b/backends/pulse/pulse-monitor.c
@@ -16,6 +16,7 @@
*/
#include <glib.h>
+#include <glib/gi18n.h>
#include <glib-object.h>
#include <pulse/pulseaudio.h>
@@ -27,7 +28,6 @@ struct _PulseMonitorPrivate
pa_context *context;
pa_proplist *proplist;
pa_stream *stream;
- gchar *name;
guint32 index_source;
guint32 index_sink_input;
gboolean enabled;
@@ -36,7 +36,6 @@ struct _PulseMonitorPrivate
enum {
PROP_0,
PROP_ENABLED,
- PROP_NAME,
PROP_INDEX_SOURCE,
PROP_INDEX_SINK_INPUT,
N_PROPERTIES
@@ -91,15 +90,6 @@ pulse_monitor_class_init (PulseMonitorClass *klass)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS);
- properties[PROP_NAME] =
- g_param_spec_string ("name",
- "Name",
- "Name of the monitor",
- NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT |
- G_PARAM_STATIC_STRINGS);
-
properties[PROP_INDEX_SOURCE] =
g_param_spec_uint ("index-source",
"Index of source",
@@ -153,9 +143,6 @@ pulse_monitor_get_property (GObject *object,
case PROP_ENABLED:
g_value_set_boolean (value, monitor->priv->enabled);
break;
- case PROP_NAME:
- g_value_set_string (value, monitor->priv->name);
- break;
case PROP_INDEX_SOURCE:
g_value_set_uint (value, monitor->priv->index_source);
break;
@@ -179,9 +166,6 @@ pulse_monitor_set_property (GObject *object,
monitor = PULSE_MONITOR (object);
switch (param_id) {
- case PROP_NAME:
- pulse_monitor_set_name (monitor, g_value_get_string (value));
- break;
case PROP_INDEX_SOURCE:
monitor->priv->index_source = g_value_get_uint (value);
break;
@@ -218,15 +202,12 @@ pulse_monitor_finalize (GObject *object)
pa_context_unref (monitor->priv->context);
pa_proplist_free (monitor->priv->proplist);
- g_free (monitor->priv->name);
-
G_OBJECT_CLASS (pulse_monitor_parent_class)->finalize (object);
}
PulseMonitor *
pulse_monitor_new (pa_context *context,
pa_proplist *proplist,
- const gchar *name,
guint32 index_source,
guint32 index_sink_input)
{
@@ -236,7 +217,6 @@ pulse_monitor_new (pa_context *context,
g_return_val_if_fail (proplist != NULL, NULL);
monitor = g_object_new (PULSE_TYPE_MONITOR,
- "name", name,
"index-source", index_source,
"index-sink-input", index_sink_input,
NULL);
@@ -280,34 +260,11 @@ pulse_monitor_set_enabled (PulseMonitor *monitor, gboolean enabled)
return TRUE;
}
-const gchar *
-pulse_monitor_get_name (PulseMonitor *monitor)
-{
- g_return_val_if_fail (PULSE_IS_MONITOR (monitor), NULL);
-
- return monitor->priv->name;
-}
-
-gboolean
-pulse_monitor_set_name (PulseMonitor *monitor, const gchar *name)
-{
- g_return_val_if_fail (PULSE_IS_MONITOR (monitor), FALSE);
-
- if (g_strcmp0 (name, monitor->priv->name) != 0) {
- g_free (monitor->priv->name);
- monitor->priv->name = g_strdup (name);
-
- g_object_notify_by_pspec (G_OBJECT (monitor), properties[PROP_NAME]);
- }
- return TRUE;
-}
-
static gboolean
stream_connect (PulseMonitor *monitor)
{
pa_sample_spec spec;
pa_buffer_attr attr;
- const gchar *name;
gchar *idx;
int ret;
@@ -320,14 +277,9 @@ stream_connect (PulseMonitor *monitor)
spec.format = PA_SAMPLE_FLOAT32;
spec.rate = 25;
- if (monitor->priv->name != NULL)
- name = monitor->priv->name;
- else
- name = "Peak detect";
-
monitor->priv->stream =
pa_stream_new_with_proplist (monitor->priv->context,
- name,
+ _("Peak detect"),
&spec,
NULL,
monitor->priv->proplist);
diff --git a/backends/pulse/pulse-monitor.h b/backends/pulse/pulse-monitor.h
index 41147f5..e371ec3 100644
--- a/backends/pulse/pulse-monitor.h
+++ b/backends/pulse/pulse-monitor.h
@@ -23,6 +23,8 @@
#include <pulse/pulseaudio.h>
+#include "pulse-types.h"
+
G_BEGIN_DECLS
#define PULSE_TYPE_MONITOR \
@@ -38,7 +40,6 @@ G_BEGIN_DECLS
#define PULSE_MONITOR_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_MONITOR, PulseMonitorClass))
-typedef struct _PulseMonitor PulseMonitor;
typedef struct _PulseMonitorClass PulseMonitorClass;
typedef struct _PulseMonitorPrivate PulseMonitorPrivate;
@@ -54,7 +55,7 @@ struct _PulseMonitorClass
{
GObjectClass parent_class;
- /* Signals */
+ /*< private >*/
void (*value) (PulseMonitor *monitor,
gdouble value);
};
@@ -63,7 +64,6 @@ GType pulse_monitor_get_type (void) G_GNUC_CONST;
PulseMonitor *pulse_monitor_new (pa_context *context,
pa_proplist *proplist,
- const gchar *name,
guint32 index_source,
guint32 index_sink_input);
@@ -71,10 +71,6 @@ gboolean pulse_monitor_get_enabled (PulseMonitor *monitor);
gboolean pulse_monitor_set_enabled (PulseMonitor *monitor,
gboolean enabled);
-const gchar * pulse_monitor_get_name (PulseMonitor *monitor);
-gboolean pulse_monitor_set_name (PulseMonitor *monitor,
- const gchar *name);
-
G_END_DECLS
#endif /* PULSE_MONITOR_H */
diff --git a/backends/pulse/pulse-port-switch.c b/backends/pulse/pulse-port-switch.c
new file mode 100644
index 0000000..08f1543
--- /dev/null
+++ b/backends/pulse/pulse-port-switch.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "pulse-connection.h"
+#include "pulse-port.h"
+#include "pulse-port-switch.h"
+#include "pulse-stream.h"
+
+struct _PulsePortSwitchPrivate
+{
+ GList *ports;
+ PulseStream *stream;
+};
+
+enum {
+ PROP_0,
+ PROP_STREAM,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void pulse_port_switch_class_init (PulsePortSwitchClass *klass);
+
+static void pulse_port_switch_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_port_switch_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_port_switch_init (PulsePortSwitch *swtch);
+static void pulse_port_switch_dispose (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE (PulsePortSwitch, pulse_port_switch, MATE_MIXER_TYPE_SWITCH)
+
+static gboolean pulse_port_switch_set_active_option (MateMixerSwitch *mms,
+ MateMixerSwitchOption *mmso);
+
+static const GList *pulse_port_switch_list_options (MateMixerSwitch *mms);
+
+static gint compare_ports (gconstpointer a,
+ gconstpointer b);
+static gint compare_port_name (gconstpointer a,
+ gconstpointer b);
+
+static void
+pulse_port_switch_class_init (PulsePortSwitchClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerSwitchClass *switch_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_port_switch_dispose;
+ object_class->get_property = pulse_port_switch_get_property;
+ object_class->set_property = pulse_port_switch_set_property;
+
+ switch_class = MATE_MIXER_SWITCH_CLASS (klass);
+ switch_class->set_active_option = pulse_port_switch_set_active_option;
+ switch_class->list_options = pulse_port_switch_list_options;
+
+ properties[PROP_STREAM] =
+ g_param_spec_object ("stream",
+ "Stream",
+ "PulseAudio stream",
+ PULSE_TYPE_STREAM,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ g_type_class_add_private (G_OBJECT_CLASS (klass), sizeof (PulsePortSwitchPrivate));
+}
+
+static void
+pulse_port_switch_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PulsePortSwitch *swtch;
+
+ swtch = PULSE_PORT_SWITCH (object);
+
+ switch (param_id) {
+ case PROP_STREAM:
+ g_value_set_object (value, swtch->priv->stream);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_port_switch_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PulsePortSwitch *swtch;
+
+ swtch = PULSE_PORT_SWITCH (object);
+
+ switch (param_id) {
+ case PROP_STREAM:
+ /* Construct-only object */
+ swtch->priv->stream = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_port_switch_init (PulsePortSwitch *swtch)
+{
+ swtch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swtch,
+ PULSE_TYPE_PORT_SWITCH,
+ PulsePortSwitchPrivate);
+}
+
+static void
+pulse_port_switch_dispose (GObject *object)
+{
+ PulsePortSwitch *swtch;
+
+ swtch = PULSE_PORT_SWITCH (object);
+
+ g_clear_object (&swtch->priv->stream);
+
+ G_OBJECT_CLASS (pulse_port_switch_parent_class)->dispose (object);
+}
+
+PulseStream *
+pulse_port_switch_get_stream (PulsePortSwitch *swtch)
+{
+ g_return_val_if_fail (PULSE_IS_PORT_SWITCH (swtch), NULL);
+
+ return swtch->priv->stream;
+}
+
+void
+pulse_port_switch_add_port (PulsePortSwitch *swtch, PulsePort *port)
+{
+ g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch));
+ g_return_if_fail (PULSE_IS_PORT (port));
+
+ swtch->priv->ports = g_list_insert_sorted (swtch->priv->ports,
+ port,
+ compare_ports);
+}
+
+void
+pulse_port_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port)
+{
+ g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch));
+ g_return_if_fail (PULSE_IS_PORT (port));
+
+ _mate_mixer_switch_set_active_option (MATE_MIXER_SWITCH (swtch),
+ MATE_MIXER_SWITCH_OPTION (port));
+}
+
+void
+pulse_port_switch_set_active_port_by_name (PulsePortSwitch *swtch, const gchar *name)
+{
+ GList *item;
+
+ g_return_if_fail (PULSE_IS_PORT_SWITCH (swtch));
+ g_return_if_fail (name != NULL);
+
+ item = g_list_find_custom (swtch->priv->ports, name, compare_port_name);
+ if G_UNLIKELY (item == NULL) {
+ g_debug ("Invalid switch port name %s", name);
+ return;
+ }
+ pulse_port_switch_set_active_port (swtch, PULSE_PORT (item->data));
+}
+
+static gboolean
+pulse_port_switch_set_active_option (MateMixerSwitch *mms, MateMixerSwitchOption *mmso)
+{
+ PulsePortSwitchClass *klass;
+
+ g_return_val_if_fail (PULSE_IS_PORT_SWITCH (mms), FALSE);
+ g_return_val_if_fail (PULSE_IS_PORT (mmso), FALSE);
+
+ klass = PULSE_PORT_SWITCH_GET_CLASS (PULSE_PORT_SWITCH (mms));
+
+ return klass->set_active_port (PULSE_PORT_SWITCH (mms),
+ PULSE_PORT (mmso));
+}
+
+static const GList *
+pulse_port_switch_list_options (MateMixerSwitch *swtch)
+{
+ g_return_val_if_fail (PULSE_IS_PORT_SWITCH (swtch), NULL);
+
+ return PULSE_PORT_SWITCH (swtch)->priv->ports;
+}
+
+static gint
+compare_ports (gconstpointer a, gconstpointer b)
+{
+ return pulse_port_get_priority (PULSE_PORT (b)) -
+ pulse_port_get_priority (PULSE_PORT (a));
+}
+
+static gint
+compare_port_name (gconstpointer a, gconstpointer b)
+{
+ PulsePort *port = PULSE_PORT (a);
+ const gchar *name = (const gchar *) b;
+
+ return strcmp (pulse_port_get_name (port), name);
+}
diff --git a/backends/pulse/pulse-port-switch.h b/backends/pulse/pulse-port-switch.h
new file mode 100644
index 0000000..6ccef38
--- /dev/null
+++ b/backends/pulse/pulse-port-switch.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_PORT_SWITCH_H
+#define PULSE_PORT_SWITCH_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_PORT_SWITCH \
+ (pulse_port_switch_get_type ())
+#define PULSE_PORT_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_PORT_SWITCH, PulsePortSwitch))
+#define PULSE_IS_PORT_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_PORT_SWITCH))
+#define PULSE_PORT_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_PORT_SWITCH, PulsePortSwitchClass))
+#define PULSE_IS_PORT_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_PORT_SWITCH))
+#define PULSE_PORT_SWITCH_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_PORT_SWITCH, PulsePortSwitchClass))
+
+typedef struct _PulsePortSwitchClass PulsePortSwitchClass;
+typedef struct _PulsePortSwitchPrivate PulsePortSwitchPrivate;
+
+struct _PulsePortSwitch
+{
+ MateMixerSwitch parent;
+
+ /*< private >*/
+ PulsePortSwitchPrivate *priv;
+};
+
+struct _PulsePortSwitchClass
+{
+ MateMixerSwitchClass parent_class;
+
+ /*< private >*/
+ gboolean (*set_active_port) (PulsePortSwitch *swtch,
+ PulsePort *port);
+};
+
+GType pulse_port_switch_get_type (void) G_GNUC_CONST;
+
+PulseStream *pulse_port_switch_get_stream (PulsePortSwitch *swtch);
+
+void pulse_port_switch_add_port (PulsePortSwitch *swtch,
+ PulsePort *port);
+
+void pulse_port_switch_set_active_port (PulsePortSwitch *swtch,
+ PulsePort *port);
+
+void pulse_port_switch_set_active_port_by_name (PulsePortSwitch *swtch,
+ const gchar *name);
+
+G_END_DECLS
+
+#endif /* PULSE_PORT_SWITCH_H */
diff --git a/backends/pulse/pulse-port.c b/backends/pulse/pulse-port.c
new file mode 100644
index 0000000..f427448
--- /dev/null
+++ b/backends/pulse/pulse-port.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-port.h"
+
+struct _PulsePortPrivate
+{
+ guint priority;
+};
+
+static void pulse_port_class_init (PulsePortClass *klass);
+static void pulse_port_init (PulsePort *port);
+
+G_DEFINE_TYPE (PulsePort, pulse_port, MATE_MIXER_TYPE_SWITCH_OPTION)
+
+static void
+pulse_port_class_init (PulsePortClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (PulsePortPrivate));
+}
+
+static void
+pulse_port_init (PulsePort *port)
+{
+ port->priv = G_TYPE_INSTANCE_GET_PRIVATE (port,
+ PULSE_TYPE_PORT,
+ PulsePortPrivate);
+}
+
+PulsePort *
+pulse_port_new (const gchar *name,
+ const gchar *label,
+ const gchar *icon,
+ guint priority)
+{
+ PulsePort *port;
+
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (label != NULL, NULL);
+
+ port = g_object_new (PULSE_TYPE_PORT,
+ "name", name,
+ "label", label,
+ "icon", icon,
+ NULL);
+
+ port->priv->priority = priority;
+ return port;
+}
+
+const gchar *
+pulse_port_get_name (PulsePort *port)
+{
+ g_return_val_if_fail (PULSE_IS_PORT (port), NULL);
+
+ return mate_mixer_switch_option_get_name (MATE_MIXER_SWITCH_OPTION (port));
+}
+
+guint
+pulse_port_get_priority (PulsePort *port)
+{
+ g_return_val_if_fail (PULSE_IS_PORT (port), 0);
+
+ return port->priv->priority;
+}
diff --git a/backends/pulse/pulse-port.h b/backends/pulse/pulse-port.h
new file mode 100644
index 0000000..241fa2d
--- /dev/null
+++ b/backends/pulse/pulse-port.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_PORT_H
+#define PULSE_PORT_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_PORT \
+ (pulse_port_get_type ())
+#define PULSE_PORT(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_PORT, PulsePort))
+#define PULSE_IS_PORT(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_PORT))
+#define PULSE_PORT_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_PORT, PulsePortClass))
+#define PULSE_IS_PORT_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_PORT))
+#define PULSE_PORT_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_IS_PORT, PulsePortClass))
+
+typedef struct _PulsePortClass PulsePortClass;
+typedef struct _PulsePortPrivate PulsePortPrivate;
+
+struct _PulsePort
+{
+ MateMixerSwitchOption parent;
+
+ /*< private >*/
+ PulsePortPrivate *priv;
+};
+
+struct _PulsePortClass
+{
+ MateMixerSwitchOptionClass parent;
+};
+
+GType pulse_port_get_type (void) G_GNUC_CONST;
+
+PulsePort * pulse_port_new (const gchar *name,
+ const gchar *label,
+ const gchar *icon,
+ guint priority);
+
+const gchar *pulse_port_get_name (PulsePort *port);
+guint pulse_port_get_priority (PulsePort *port);
+
+G_END_DECLS
+
+#endif /* PULSE_PORT_H */
diff --git a/backends/pulse/pulse-sink-control.c b/backends/pulse/pulse-sink-control.c
new file mode 100644
index 0000000..500cef0
--- /dev/null
+++ b/backends/pulse/pulse-sink-control.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-monitor.h"
+#include "pulse-stream-control.h"
+#include "pulse-sink.h"
+#include "pulse-sink-control.h"
+
+static void pulse_sink_control_class_init (PulseSinkControlClass *klass);
+static void pulse_sink_control_init (PulseSinkControl *control);
+
+G_DEFINE_TYPE (PulseSinkControl, pulse_sink_control, PULSE_TYPE_STREAM_CONTROL);
+
+static gboolean pulse_sink_control_set_mute (PulseStreamControl *psc,
+ gboolean mute);
+static gboolean pulse_sink_control_set_volume (PulseStreamControl *psc,
+ pa_cvolume *cvolume);
+static PulseMonitor *pulse_sink_control_create_monitor (PulseStreamControl *psc);
+
+static void
+pulse_sink_control_class_init (PulseSinkControlClass *klass)
+{
+ PulseStreamControlClass *control_class;
+
+ control_class = PULSE_STREAM_CONTROL_CLASS (klass);
+ control_class->set_mute = pulse_sink_control_set_mute;
+ control_class->set_volume = pulse_sink_control_set_volume;
+ control_class->create_monitor = pulse_sink_control_create_monitor;
+}
+
+static void
+pulse_sink_control_init (PulseSinkControl *control)
+{
+}
+
+PulseSinkControl *
+pulse_sink_control_new (PulseSink *sink,
+ const pa_sink_info *info)
+{
+ PulseSinkControl *control;
+ MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
+ MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
+ MateMixerStreamControlRole role;
+ guint32 index;
+
+ g_return_val_if_fail (PULSE_IS_SINK (sink), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (info->active_port != NULL)
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_PORT;
+ else
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_MASTER;
+
+ /* Build the flag list */
+ if (info->flags & PA_SINK_DECIBEL_VOLUME)
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
+
+ index = pulse_sink_get_index_monitor (sink);
+ if (index != PA_INVALID_INDEX)
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_MONITOR;
+
+ control = g_object_new (PULSE_TYPE_SINK_CONTROL,
+ "name", info->name,
+ "label", info->description,
+ "flags", flags,
+ "role", role,
+ "stream", sink,
+ NULL);
+
+ pulse_sink_control_update (control, info);
+ return control;
+}
+
+void
+pulse_sink_control_update (PulseSinkControl *control, const pa_sink_info *info)
+{
+ g_return_if_fail (PULSE_IS_SINK_CONTROL (control));
+ g_return_if_fail (info != NULL);
+
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (control));
+
+ _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (control),
+ info->mute ? TRUE : FALSE);
+
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (control),
+ &info->channel_map);
+
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (control),
+ &info->volume,
+ info->base_volume);
+
+ g_object_thaw_notify (G_OBJECT (control));
+}
+
+static gboolean
+pulse_sink_control_set_mute (PulseStreamControl *psc, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), FALSE);
+
+ return pulse_connection_set_sink_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc),
+ mute);
+}
+
+static gboolean
+pulse_sink_control_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume)
+{
+ g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
+
+ return pulse_connection_set_sink_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc),
+ cvolume);
+}
+
+static PulseMonitor *
+pulse_sink_control_create_monitor (PulseStreamControl *psc)
+{
+ PulseSink *sink;
+ guint32 index;
+
+ g_return_val_if_fail (PULSE_IS_SINK_CONTROL (psc), NULL);
+
+ sink = PULSE_SINK (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc)));
+
+ index = pulse_sink_get_index_monitor (sink);
+ if G_UNLIKELY (index == PA_INVALID_INDEX) {
+ g_debug ("Monitor of stream control %s is not available",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc)));
+ return NULL;
+ }
+
+ return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ index,
+ PA_INVALID_INDEX);
+}
diff --git a/backends/pulse/pulse-sink-control.h b/backends/pulse/pulse-sink-control.h
new file mode 100644
index 0000000..e9570f4
--- /dev/null
+++ b/backends/pulse/pulse-sink-control.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_SINK_CONTROL_H
+#define PULSE_SINK_CONTROL_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-stream-control.h"
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_SINK_CONTROL \
+ (pulse_sink_control_get_type ())
+#define PULSE_SINK_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_CONTROL, PulseSinkControl))
+#define PULSE_IS_SINK_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_CONTROL))
+#define PULSE_SINK_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_CONTROL, PulseSinkControlClass))
+#define PULSE_IS_SINK_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_CONTROL))
+#define PULSE_SINK_CONTROL_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_CONTROL, PulseSinkControlClass))
+
+typedef struct _PulseSinkControlClass PulseSinkControlClass;
+typedef struct _PulseSinkControlPrivate PulseSinkControlPrivate;
+
+struct _PulseSinkControl
+{
+ PulseStreamControl parent;
+};
+
+struct _PulseSinkControlClass
+{
+ PulseStreamControlClass parent_class;
+};
+
+GType pulse_sink_control_get_type (void) G_GNUC_CONST;
+
+PulseSinkControl *pulse_sink_control_new (PulseSink *sink,
+ const pa_sink_info *info);
+
+void pulse_sink_control_update (PulseSinkControl *control,
+ const pa_sink_info *info);
+
+G_END_DECLS
+
+#endif /* PULSE_SINK_CONTROL_H */
diff --git a/backends/pulse/pulse-sink-input.c b/backends/pulse/pulse-sink-input.c
index 1d5f9c2..eab85b8 100644
--- a/backends/pulse/pulse-sink-input.c
+++ b/backends/pulse/pulse-sink-input.c
@@ -15,57 +15,41 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
#include <glib.h>
#include <glib-object.h>
-
-#include <libmatemixer/matemixer-client-stream.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
-#include "pulse-client-stream.h"
#include "pulse-helpers.h"
#include "pulse-monitor.h"
#include "pulse-sink.h"
#include "pulse-sink-input.h"
#include "pulse-stream.h"
+#include "pulse-stream-control.h"
static void pulse_sink_input_class_init (PulseSinkInputClass *klass);
static void pulse_sink_input_init (PulseSinkInput *input);
-G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_CLIENT_STREAM);
-
-static void pulse_sink_input_reload (PulseStream *pstream);
+G_DEFINE_TYPE (PulseSinkInput, pulse_sink_input, PULSE_TYPE_STREAM_CONTROL);
-static gboolean pulse_sink_input_set_mute (PulseStream *pstream,
- gboolean mute);
-static gboolean pulse_sink_input_set_volume (PulseStream *pstream,
- pa_cvolume *cvolume);
-static gboolean pulse_sink_input_set_parent (PulseClientStream *pclient,
- PulseStream *parent);
-static gboolean pulse_sink_input_remove (PulseClientStream *pclient);
-static PulseMonitor *pulse_sink_input_create_monitor (PulseStream *pstream);
+static gboolean pulse_sink_input_set_mute (PulseStreamControl *psc,
+ gboolean mute);
+static gboolean pulse_sink_input_set_volume (PulseStreamControl *psc,
+ pa_cvolume *cvolume);
+static PulseMonitor *pulse_sink_input_create_monitor (PulseStreamControl *psc);
static void
pulse_sink_input_class_init (PulseSinkInputClass *klass)
{
- PulseStreamClass *stream_class;
- PulseClientStreamClass *client_class;
-
- stream_class = PULSE_STREAM_CLASS (klass);
+ PulseStreamControlClass *control_class;
- stream_class->reload = pulse_sink_input_reload;
- stream_class->set_mute = pulse_sink_input_set_mute;
- stream_class->set_volume = pulse_sink_input_set_volume;
- stream_class->create_monitor = pulse_sink_input_create_monitor;
-
- client_class = PULSE_CLIENT_STREAM_CLASS (klass);
-
- client_class->set_parent = pulse_sink_input_set_parent;
- client_class->remove = pulse_sink_input_remove;
+ control_class = PULSE_STREAM_CONTROL_CLASS (klass);
+ control_class->set_mute = pulse_sink_input_set_mute;
+ control_class->set_volume = pulse_sink_input_set_volume;
+ control_class->create_monitor = pulse_sink_input_create_monitor;
}
static void
@@ -73,230 +57,171 @@ pulse_sink_input_init (PulseSinkInput *input)
{
}
-PulseStream *
-pulse_sink_input_new (PulseConnection *connection,
- const pa_sink_input_info *info,
- PulseStream *parent)
+PulseSinkInput *
+pulse_sink_input_new (PulseSink *sink, const pa_sink_input_info *info)
{
- PulseSinkInput *input;
+ PulseSinkInput *input;
+ gchar *name;
+ const gchar *prop;
+ const gchar *label = NULL;
+ MateMixerAppInfo *app_info = NULL;
- g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
- g_return_val_if_fail (info != NULL, NULL);
+ MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
+ MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_MONITOR;
+ MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
- /* Consider the sink input index as unchanging parameter */
- input = g_object_new (PULSE_TYPE_SINK_INPUT,
- "connection", connection,
- "index", info->index,
- NULL);
+ MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN;
- /* Other data may change at any time, so let's make a use of our update function */
- pulse_sink_input_update (PULSE_STREAM (input), info, parent);
-
- return PULSE_STREAM (input);
-}
-
-gboolean
-pulse_sink_input_update (PulseStream *pstream,
- const pa_sink_input_info *info,
- PulseStream *parent)
-{
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
- MATE_MIXER_STREAM_CLIENT |
- MATE_MIXER_STREAM_HAS_MUTE |
- MATE_MIXER_STREAM_HAS_MONITOR;
- PulseClientStream *pclient;
- const gchar *prop;
- const gchar *description = NULL;
- gchar *name;
-
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
-
- pclient = PULSE_CLIENT_STREAM (pstream);
-
- /* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (pstream));
+ g_return_val_if_fail (PULSE_IS_SINK (sink), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
/* Many mixer applications query the Pulse client list and use the client
* name here, but we use the name only as an identifier, so let's avoid
* this unnecessary overhead and use a custom name.
* Also make sure to make the name unique by including the PulseAudio index. */
- name = g_strdup_printf ("pulse-stream-client-output-%lu", (gulong) info->index);
-
- pulse_stream_update_name (pstream, name);
- g_free (name);
-
- prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
- if (prop != NULL) {
- MateMixerClientStreamRole role = pulse_convert_media_role_name (prop);
-
- if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) {
- /* The event description seems to provide much better readable
- * description for event streams */
- prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
-
- if (G_LIKELY (prop != NULL))
- description = prop;
- }
- pulse_client_stream_update_role (pclient, role);
- } else
- pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE);
-
- if (description == NULL)
- description = info->name;
-
- pulse_stream_update_description (pstream, description);
- pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
-
- if (info->client != PA_INVALID_INDEX)
- pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION);
- else
- pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS);
-
- if (G_LIKELY (parent != NULL)) {
- if (pulse_sink_get_monitor_index (parent) != PA_INVALID_INDEX)
- flags |= MATE_MIXER_STREAM_HAS_MONITOR;
-
- pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent));
- } else
- pulse_client_stream_update_parent (pclient, NULL);
+ name = g_strdup_printf ("pulse-output-control-%lu", (gulong) info->index);
#if PA_CHECK_VERSION(1, 0, 0)
if (info->has_volume) {
flags |=
- MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME;
+ MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
if (info->volume_writable)
- flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
}
-
- /* Flags needed before volume */
- pulse_stream_update_flags (pstream, flags);
- pulse_stream_update_channel_map (pstream, &info->channel_map);
-
- if (info->has_volume)
- pulse_stream_update_volume (pstream, &info->volume, 0);
- else
- pulse_stream_update_volume (pstream, NULL, 0);
#else
/* Pre-1.0 PulseAudio does not include the has_volume and volume_writable
* fields, but does include the volume info, so let's give it a try */
flags |=
- MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME |
- MATE_MIXER_STREAM_CAN_SET_VOLUME;
+ MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
+#endif
- /* Flags needed before volume */
- pulse_stream_update_flags (pstream, flags);
- pulse_stream_update_channel_map (pstream, &info->channel_map);
+ if (info->client != PA_INVALID_INDEX) {
+ app_info = _mate_mixer_app_info_new ();
- pulse_stream_update_volume (pstream, &info->volume, 0);
-#endif
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION;
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
- if (prop != NULL)
- pulse_client_stream_update_app_name (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_name (app_info, prop);
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
- if (prop != NULL)
- pulse_client_stream_update_app_id (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_id (app_info, prop);
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
- if (prop != NULL)
- pulse_client_stream_update_app_version (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_version (app_info, prop);
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
- if (prop != NULL)
- pulse_client_stream_update_app_icon (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_icon (app_info, prop);
+ }
- // XXX needs to fix monitor if parent changes
+ prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
+ if (prop != NULL) {
+ media_role = pulse_convert_media_role_name (prop);
- g_object_thaw_notify (G_OBJECT (pstream));
- return TRUE;
-}
+ if (media_role == MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_EVENT) {
+ /* The event description seems to provide much better readable
+ * description for event streams */
+ prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
+ if (prop != NULL)
+ label = prop;
+ }
+ }
-static void
-pulse_sink_input_reload (PulseStream *pstream)
-{
- g_return_if_fail (PULSE_IS_SINK_INPUT (pstream));
+ if (label == NULL)
+ label = info->name;
- pulse_connection_load_sink_input_info (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream));
-}
+ input = g_object_new (PULSE_TYPE_SINK_INPUT,
+ "name", name,
+ "label", label,
+ "flags", flags,
+ "role", role,
+ "media-role", media_role,
+ "index", info->index,
+ "stream", sink,
+ NULL);
+ g_free (name);
-static gboolean
-pulse_sink_input_set_mute (PulseStream *pstream, gboolean mute)
-{
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE);
+ if (app_info != NULL)
+ pulse_stream_control_set_app_info (PULSE_STREAM_CONTROL (input), app_info);
- return pulse_connection_set_sink_input_mute (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- mute);
+ pulse_sink_input_update (input, info);
+ return input;
}
-static gboolean
-pulse_sink_input_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
+void
+pulse_sink_input_update (PulseSinkInput *input, const pa_sink_input_info *info)
{
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), FALSE);
- g_return_val_if_fail (cvolume != NULL, FALSE);
+ g_return_if_fail (PULSE_IS_SINK_INPUT (input));
+ g_return_if_fail (info != NULL);
- return pulse_connection_set_sink_input_volume (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- cvolume);
-}
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (input));
-static gboolean
-pulse_sink_input_set_parent (PulseClientStream *pclient, PulseStream *parent)
-{
- PulseStream *pstream;
+ _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (input),
+ info->mute ? TRUE : FALSE);
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE);
+#if PA_CHECK_VERSION(1, 0, 0)
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input), &info->channel_map);
- pstream = PULSE_STREAM (pclient);
+ if (info->has_volume)
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input), &info->volume, 0);
+ else
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (input), NULL, 0);
+#else
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (input), &info->channel_map);
+ pulse_stream_control_set_volume (PULSE_STREAM_CONTROL (input), &info->volume, 0);
+#endif
- return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- pulse_stream_get_index (parent));
+ g_object_thaw_notify (G_OBJECT (input));
}
static gboolean
-pulse_sink_input_remove (PulseClientStream *pclient)
+pulse_sink_input_set_mute (PulseStreamControl *psc, gboolean mute)
{
- PulseStream *pstream;
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), FALSE);
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (pclient), FALSE);
+ return pulse_connection_set_sink_input_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ pulse_stream_control_get_index (psc),
+ mute);
+}
- pstream = PULSE_STREAM (pclient);
+static gboolean
+pulse_sink_input_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume)
+{
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
- return pulse_connection_kill_sink_input (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream));
+ return pulse_connection_set_sink_input_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ pulse_stream_control_get_index (psc),
+ cvolume);
}
static PulseMonitor *
-pulse_sink_input_create_monitor (PulseStream *pstream)
+pulse_sink_input_create_monitor (PulseStreamControl *psc)
{
- MateMixerStream *parent;
- guint32 index;
-
- g_return_val_if_fail (PULSE_IS_SINK_INPUT (pstream), NULL);
+ PulseSink *sink;
+ guint32 index;
- parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
- if (G_UNLIKELY (parent == NULL)) {
- g_debug ("Not creating monitor for client stream %s as it is not available",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
- return NULL;
- }
+ g_return_val_if_fail (PULSE_IS_SINK_INPUT (psc), NULL);
- index = pulse_sink_get_monitor_index (PULSE_STREAM (parent));
+ sink = PULSE_SINK (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc)));
- if (G_UNLIKELY (index == PA_INVALID_INDEX)) {
- g_debug ("Not creating monitor for client stream %s as it is not available",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
+ index = pulse_sink_get_index_monitor (sink);
+ if G_UNLIKELY (index == PA_INVALID_INDEX) {
+ g_debug ("Monitor of stream control %s is not available",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc)));
return NULL;
}
- return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
+ return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
index,
- pulse_stream_get_index (pstream));
+ pulse_stream_control_get_index (psc));
}
diff --git a/backends/pulse/pulse-sink-input.h b/backends/pulse/pulse-sink-input.h
index 1e5004b..127eab6 100644
--- a/backends/pulse/pulse-sink-input.h
+++ b/backends/pulse/pulse-sink-input.h
@@ -23,9 +23,8 @@
#include <pulse/pulseaudio.h>
-#include "pulse-client-stream.h"
-#include "pulse-connection.h"
-#include "pulse-stream.h"
+#include "pulse-stream-control.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
@@ -42,28 +41,25 @@ G_BEGIN_DECLS
#define PULSE_SINK_INPUT_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_INPUT, PulseSinkInputClass))
-typedef struct _PulseSinkInput PulseSinkInput;
typedef struct _PulseSinkInputClass PulseSinkInputClass;
struct _PulseSinkInput
{
- PulseClientStream parent;
+ PulseStreamControl parent;
};
struct _PulseSinkInputClass
{
- PulseClientStreamClass parent_class;
+ PulseStreamControlClass parent_class;
};
-GType pulse_sink_input_get_type (void) G_GNUC_CONST;
+GType pulse_sink_input_get_type (void) G_GNUC_CONST;
-PulseStream *pulse_sink_input_new (PulseConnection *connection,
- const pa_sink_input_info *info,
- PulseStream *parent);
+PulseSinkInput *pulse_sink_input_new (PulseSink *sink,
+ const pa_sink_input_info *info);
-gboolean pulse_sink_input_update (PulseStream *pstream,
- const pa_sink_input_info *info,
- PulseStream *parent);
+void pulse_sink_input_update (PulseSinkInput *input,
+ const pa_sink_input_info *info);
G_END_DECLS
diff --git a/backends/pulse/pulse-sink-switch.c b/backends/pulse/pulse-sink-switch.c
new file mode 100644
index 0000000..0e08dac
--- /dev/null
+++ b/backends/pulse/pulse-sink-switch.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "pulse-connection.h"
+#include "pulse-port.h"
+#include "pulse-port-switch.h"
+#include "pulse-sink-switch.h"
+#include "pulse-stream.h"
+
+static void pulse_sink_switch_class_init (PulseSinkSwitchClass *klass);
+static void pulse_sink_switch_init (PulseSinkSwitch *swtch);
+
+G_DEFINE_TYPE (PulseSinkSwitch, pulse_sink_switch, PULSE_TYPE_PORT_SWITCH)
+
+static gboolean pulse_sink_switch_set_active_port (PulsePortSwitch *swtch,
+ PulsePort *port);
+
+static void
+pulse_sink_switch_class_init (PulseSinkSwitchClass *klass)
+{
+ PulsePortSwitchClass *switch_class;
+
+ switch_class = PULSE_PORT_SWITCH_CLASS (klass);
+ switch_class->set_active_port = pulse_sink_switch_set_active_port;
+}
+
+static void
+pulse_sink_switch_init (PulseSinkSwitch *swtch)
+{
+}
+
+PulsePortSwitch *
+pulse_sink_switch_new (const gchar *name, const gchar *label, PulseSink *sink)
+{
+ return g_object_new (PULSE_TYPE_SINK_SWITCH,
+ "name", name,
+ "label", label,
+ "role", MATE_MIXER_SWITCH_ROLE_PORT,
+ "stream", sink,
+ NULL);
+}
+
+static gboolean
+pulse_sink_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port)
+{
+ PulseStream *stream;
+
+ g_return_val_if_fail (PULSE_IS_SINK_SWITCH (swtch), FALSE);
+ g_return_val_if_fail (PULSE_IS_PORT (port), FALSE);
+
+ stream = pulse_port_switch_get_stream (swtch);
+
+ return pulse_connection_set_sink_port (pulse_stream_get_connection (stream),
+ pulse_stream_get_index (stream),
+ pulse_port_get_name (port));
+}
diff --git a/backends/pulse/pulse-sink-switch.h b/backends/pulse/pulse-sink-switch.h
new file mode 100644
index 0000000..71ed4c9
--- /dev/null
+++ b/backends/pulse/pulse-sink-switch.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_SINK_SWITCH_H
+#define PULSE_SINK_SWITCH_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_SINK_SWITCH \
+ (pulse_sink_switch_get_type ())
+#define PULSE_SINK_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitch))
+#define PULSE_IS_SINK_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SINK_SWITCH))
+#define PULSE_SINK_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitchClass))
+#define PULSE_IS_SINK_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SINK_SWITCH))
+#define PULSE_SINK_SWITCH_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK_SWITCH, PulseSinkSwitchClass))
+
+typedef struct _PulseSinkSwitchClass PulseSinkSwitchClass;
+typedef struct _PulseSinkSwitchPrivate PulseSinkSwitchPrivate;
+
+struct _PulseSinkSwitch
+{
+ PulsePortSwitch parent;
+};
+
+struct _PulseSinkSwitchClass
+{
+ PulsePortSwitchClass parent_class;
+};
+
+GType pulse_sink_switch_get_type (void) G_GNUC_CONST;
+
+PulsePortSwitch *pulse_sink_switch_new (const gchar *name,
+ const gchar *label,
+ PulseSink *sink);
+
+G_END_DECLS
+
+#endif /* PULSE_SINK_SWITCH_H */
diff --git a/backends/pulse/pulse-sink.c b/backends/pulse/pulse-sink.c
index 0f828b1..d2f0280 100644
--- a/backends/pulse/pulse-sink.c
+++ b/backends/pulse/pulse-sink.c
@@ -16,62 +16,57 @@
*/
#include <glib.h>
+#include <glib/gi18n.h>
#include <glib-object.h>
-
-#include <libmatemixer/matemixer-port.h>
-#include <libmatemixer/matemixer-port-private.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
#include "pulse-device.h"
#include "pulse-monitor.h"
+#include "pulse-port.h"
+#include "pulse-port-switch.h"
#include "pulse-stream.h"
#include "pulse-sink.h"
+#include "pulse-sink-control.h"
+#include "pulse-sink-input.h"
+#include "pulse-sink-switch.h"
struct _PulseSinkPrivate
{
- guint32 index_monitor;
+ guint32 monitor;
+ GHashTable *inputs;
+ PulsePortSwitch *pswitch;
+ GList *streams_list;
+ GList *switches_list;
+ PulseSinkControl *control;
};
static void pulse_sink_class_init (PulseSinkClass *klass);
static void pulse_sink_init (PulseSink *sink);
+static void pulse_sink_dispose (GObject *object);
+static void pulse_sink_finalize (GObject *object);
G_DEFINE_TYPE (PulseSink, pulse_sink, PULSE_TYPE_STREAM);
-static void pulse_sink_reload (PulseStream *pstream);
-
-static gboolean pulse_sink_set_mute (PulseStream *pstream,
- gboolean mute);
-static gboolean pulse_sink_set_volume (PulseStream *pstream,
- pa_cvolume *cvolume);
-static gboolean pulse_sink_set_active_port (PulseStream *pstream,
- MateMixerPort *port);
-
-static gboolean pulse_sink_suspend (PulseStream *pstream);
-static gboolean pulse_sink_resume (PulseStream *pstream);
-
-static PulseMonitor *pulse_sink_create_monitor (PulseStream *pstream);
-
-static void update_ports (PulseStream *pstream,
- pa_sink_port_info **ports,
- pa_sink_port_info *active);
+static const GList *pulse_sink_list_controls (MateMixerStream *mms);
+static const GList *pulse_sink_list_switches (MateMixerStream *mms);
static void
pulse_sink_class_init (PulseSinkClass *klass)
{
- PulseStreamClass *stream_class;
+ GObjectClass *object_class;
+ MateMixerStreamClass *stream_class;
- stream_class = PULSE_STREAM_CLASS (klass);
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_sink_dispose;
+ object_class->finalize = pulse_sink_finalize;
- stream_class->reload = pulse_sink_reload;
- stream_class->set_mute = pulse_sink_set_mute;
- stream_class->set_volume = pulse_sink_set_volume;
- stream_class->set_active_port = pulse_sink_set_active_port;
- stream_class->suspend = pulse_sink_suspend;
- stream_class->resume = pulse_sink_resume;
- stream_class->create_monitor = pulse_sink_create_monitor;
+ stream_class = MATE_MIXER_STREAM_CLASS (klass);
+ stream_class->list_controls = pulse_sink_list_controls;
+ stream_class->list_switches = pulse_sink_list_switches;
g_type_class_add_private (klass, sizeof (PulseSinkPrivate));
}
@@ -83,247 +78,190 @@ pulse_sink_init (PulseSink *sink)
PULSE_TYPE_SINK,
PulseSinkPrivate);
- sink->priv->index_monitor = PA_INVALID_INDEX;
+ sink->priv->inputs = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+
+ sink->priv->monitor = PA_INVALID_INDEX;
}
-PulseStream *
-pulse_sink_new (PulseConnection *connection,
- const pa_sink_info *info,
- PulseDevice *device)
+static void
+pulse_sink_dispose (GObject *object)
{
- PulseStream *stream;
+ PulseSink *sink;
- g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
- g_return_val_if_fail (info != NULL, NULL);
+ sink = PULSE_SINK (object);
- /* Consider the sink index as unchanging parameter */
- stream = g_object_new (PULSE_TYPE_SINK,
- "connection", connection,
- "index", info->index,
- NULL);
+ g_clear_object (&sink->priv->control);
+ g_clear_object (&sink->priv->pswitch);
- /* Other data may change at any time, so let's make a use of our update function */
- pulse_sink_update (stream, info, device);
+ g_hash_table_remove_all (sink->priv->inputs);
- return stream;
+ G_OBJECT_CLASS (pulse_sink_parent_class)->dispose (object);
}
-guint32
-pulse_sink_get_monitor_index (PulseStream *pstream)
+static void
+pulse_sink_finalize (GObject *object)
{
- g_return_val_if_fail (PULSE_IS_SINK (pstream), PA_INVALID_INDEX);
+ PulseSink *sink;
+
+ sink = PULSE_SINK (object);
- return PULSE_SINK (pstream)->priv->index_monitor;
+ g_hash_table_unref (sink->priv->inputs);
+
+ G_OBJECT_CLASS (pulse_sink_parent_class)->finalize (object);
}
-gboolean
-pulse_sink_update (PulseStream *pstream, const pa_sink_info *info, PulseDevice *device)
+PulseSink *
+pulse_sink_new (PulseConnection *connection,
+ const pa_sink_info *info,
+ PulseDevice *device)
{
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_OUTPUT |
- MATE_MIXER_STREAM_HAS_MUTE |
- MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_CAN_SET_VOLUME |
- MATE_MIXER_STREAM_CAN_SUSPEND;
PulseSink *sink;
- g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
-
- /* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (pstream));
-
- pulse_stream_update_name (pstream, info->name);
- pulse_stream_update_description (pstream, info->description);
- pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
-
- /* Stream state */
- switch (info->state) {
- case PA_SINK_RUNNING:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING);
- break;
- case PA_SINK_IDLE:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE);
- break;
- case PA_SINK_SUSPENDED:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED);
- break;
- default:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN);
- break;
- }
-
- /* Build the flag list */
- if (info->flags & PA_SINK_DECIBEL_VOLUME)
- flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME;
- if (info->flags & PA_SINK_FLAT_VOLUME)
- flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME;
-
- sink = PULSE_SINK (pstream);
-
- if (sink->priv->index_monitor == PA_INVALID_INDEX)
- sink->priv->index_monitor = info->monitor_source;
+ g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
- if (sink->priv->index_monitor != PA_INVALID_INDEX)
- flags |= MATE_MIXER_STREAM_HAS_MONITOR;
+ sink = g_object_new (PULSE_TYPE_SINK,
+ "name", info->name,
+ "label", info->description,
+ "device", device,
+ "direction", MATE_MIXER_DIRECTION_OUTPUT,
+ "connection", connection,
+ "index", info->index,
+ NULL);
+
+ sink->priv->control = pulse_sink_control_new (sink, info);
+
+ if (info->n_ports > 0) {
+ pa_sink_port_info **ports = info->ports;
+
+ /* Create the port switch */
+ sink->priv->pswitch = pulse_sink_switch_new ("port", _("Port"), sink);
+
+ while (*ports != NULL) {
+ pa_sink_port_info *p = *ports++;
+ PulsePort *port;
+ const gchar *icon = NULL;
+
+ /* A port may include an icon but in PulseAudio sink and source ports
+ * the property is not included, for this reason ports are also read from
+ * devices where the icons may be present */
+ if (device != NULL) {
+ port = pulse_device_get_port (PULSE_DEVICE (device), p->name);
+ if (port != NULL)
+ icon = mate_mixer_switch_option_get_icon (MATE_MIXER_SWITCH_OPTION (port));
+ }
- /* Flags must be updated before volume */
- pulse_stream_update_flags (pstream, flags);
+ port = pulse_port_new (p->name,
+ p->description,
+ icon,
+ p->priority);
- pulse_stream_update_channel_map (pstream, &info->channel_map);
- pulse_stream_update_volume (pstream, &info->volume, info->base_volume);
+ pulse_port_switch_add_port (sink->priv->pswitch, port);
- pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device));
+ if (p == info->active_port)
+ pulse_port_switch_set_active_port (sink->priv->pswitch, port);
+ }
- /* Ports must be updated after device */
- if (info->ports != NULL) {
- update_ports (pstream, info->ports, info->active_port);
+ g_debug ("Created port list for sink %s", info->name);
}
- g_object_thaw_notify (G_OBJECT (pstream));
- return TRUE;
-}
-
-static void
-pulse_sink_reload (PulseStream *pstream)
-{
- g_return_if_fail (PULSE_IS_SINK (pstream));
+ pulse_sink_update (sink, info);
- pulse_connection_load_sink_info (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream));
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (sink),
+ MATE_MIXER_STREAM_CONTROL (sink->priv->control));
+ return sink;
}
-static gboolean
-pulse_sink_set_mute (PulseStream *pstream, gboolean mute)
+void
+pulse_sink_add_input (PulseSink *sink, const pa_sink_input_info *info)
{
- g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
-
- return pulse_connection_set_sink_mute (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- mute);
+ PulseSinkInput *input;
+
+ /* This function is used for both creating and refreshing sink inputs */
+ input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (info->index));
+ if (input == NULL) {
+ const gchar *name;
+
+ input = pulse_sink_input_new (sink, info);
+ g_hash_table_insert (sink->priv->inputs,
+ GINT_TO_POINTER (info->index),
+ input);
+
+ name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input));
+ g_signal_emit_by_name (G_OBJECT (sink),
+ "control-added",
+ name);
+ } else
+ pulse_sink_input_update (input, info);
}
-static gboolean
-pulse_sink_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
+void
+pulse_sink_remove_input (PulseSink *sink, guint32 index)
{
- g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
- g_return_val_if_fail (cvolume != NULL, FALSE);
+ PulseSinkInput *input;
+ const gchar *name;
- return pulse_connection_set_sink_volume (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- cvolume);
-}
+ input = g_hash_table_lookup (sink->priv->inputs, GINT_TO_POINTER (index));
+ if G_UNLIKELY (input == NULL)
+ return;
-static gboolean
-pulse_sink_set_active_port (PulseStream *pstream, MateMixerPort *port)
-{
- g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
- g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
+ name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (input));
- return pulse_connection_set_sink_port (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- mate_mixer_port_get_name (port));
+ g_hash_table_remove (sink->priv->inputs, GINT_TO_POINTER (index));
+ g_signal_emit_by_name (G_OBJECT (sink),
+ "control-removed",
+ name);
}
-static gboolean
-pulse_sink_suspend (PulseStream *pstream)
+void
+pulse_sink_update (PulseSink *sink, const pa_sink_info *info)
{
- g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
+ g_return_if_fail (PULSE_IS_SINK (sink));
+ g_return_if_fail (info != NULL);
- return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- TRUE);
+ /* The switch doesn't allow being unset, PulseAudio should always include
+ * the active port name if the are any ports available */
+ if (info->active_port != NULL)
+ pulse_port_switch_set_active_port_by_name (sink->priv->pswitch,
+ info->active_port->name);
+
+ sink->priv->monitor = info->monitor_source;
}
-static gboolean
-pulse_sink_resume (PulseStream *pstream)
+guint32
+pulse_sink_get_index_monitor (PulseSink *sink)
{
- g_return_val_if_fail (PULSE_IS_SINK (pstream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SINK (sink), 0);
- return pulse_connection_suspend_sink (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- FALSE);
+ return sink->priv->monitor;
}
-static PulseMonitor *
-pulse_sink_create_monitor (PulseStream *pstream)
+static const GList *
+pulse_sink_list_controls (MateMixerStream *mms)
{
- guint32 index;
+ GList *list;
- g_return_val_if_fail (PULSE_IS_SINK (pstream), NULL);
+ g_return_val_if_fail (PULSE_IS_SINK (mms), NULL);
- index = pulse_sink_get_monitor_index (pstream);
+ // XXX
+ list = g_hash_table_get_values (PULSE_SINK (mms)->priv->inputs);
+ if (list != NULL)
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
- if (G_UNLIKELY (index == PA_INVALID_INDEX)) {
- g_debug ("Not creating monitor for stream %s: not available",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
- return NULL;
- }
-
- return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
- index,
- PA_INVALID_INDEX);
+ return g_list_prepend (list, g_object_ref (PULSE_SINK (mms)->priv->control));
}
-static void
-update_ports (PulseStream *pstream,
- pa_sink_port_info **ports,
- pa_sink_port_info *active)
+static const GList *
+pulse_sink_list_switches (MateMixerStream *mms)
{
- MateMixerPort *port;
- MateMixerDevice *device;
- GHashTable *hash;
-
- hash = pulse_stream_get_ports (pstream);
-
- while (*ports != NULL) {
- MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
- pa_sink_port_info *info = *ports;
- const gchar *icon = NULL;
-
- device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream));
- if (device != NULL) {
- port = mate_mixer_device_get_port (device, info->name);
-
- if (port != NULL) {
- flags = mate_mixer_port_get_flags (port);
- icon = mate_mixer_port_get_icon (port);
- }
- }
-
-#if PA_CHECK_VERSION(2, 0, 0)
- if (info->available == PA_PORT_AVAILABLE_YES)
- flags |= MATE_MIXER_PORT_AVAILABLE;
- else
- flags &= ~MATE_MIXER_PORT_AVAILABLE;
-#endif
-
- port = g_hash_table_lookup (hash, info->name);
-
- if (port != NULL) {
- /* Update existing port */
- _mate_mixer_port_update_description (port, info->description);
- _mate_mixer_port_update_icon (port, icon);
- _mate_mixer_port_update_priority (port, info->priority);
- _mate_mixer_port_update_flags (port, flags);
- } else {
- /* Add previously unknown port to the hash table */
- port = _mate_mixer_port_new (info->name,
- info->description,
- icon,
- info->priority,
- flags);
-
- g_hash_table_insert (hash, g_strdup (info->name), port);
- }
-
- ports++;
- }
+ g_return_val_if_fail (PULSE_IS_SINK (mms), NULL);
- /* Active port */
- if (G_LIKELY (active != NULL))
- port = g_hash_table_lookup (hash, active->name);
- else
- port = NULL;
+ // XXX
+ if (PULSE_SINK (mms)->priv->pswitch != NULL)
+ return g_list_prepend (NULL, PULSE_SINK (mms)->priv->pswitch);
- pulse_stream_update_active_port (pstream, port);
+ return NULL;
}
diff --git a/backends/pulse/pulse-sink.h b/backends/pulse/pulse-sink.h
index c0631ca..5eaeaa0 100644
--- a/backends/pulse/pulse-sink.h
+++ b/backends/pulse/pulse-sink.h
@@ -23,9 +23,8 @@
#include <pulse/pulseaudio.h>
-#include "pulse-connection.h"
-#include "pulse-device.h"
#include "pulse-stream.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
@@ -42,7 +41,6 @@ G_BEGIN_DECLS
#define PULSE_SINK_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SINK, PulseSinkClass))
-typedef struct _PulseSink PulseSink;
typedef struct _PulseSinkClass PulseSinkClass;
typedef struct _PulseSinkPrivate PulseSinkPrivate;
@@ -59,17 +57,21 @@ struct _PulseSinkClass
PulseStreamClass parent_class;
};
-GType pulse_sink_get_type (void) G_GNUC_CONST;
+GType pulse_sink_get_type (void) G_GNUC_CONST;
-PulseStream *pulse_sink_new (PulseConnection *connection,
- const pa_sink_info *info,
- PulseDevice *device);
+PulseSink *pulse_sink_new (PulseConnection *connection,
+ const pa_sink_info *info,
+ PulseDevice *device);
-guint32 pulse_sink_get_monitor_index (PulseStream *pstream);
+void pulse_sink_add_input (PulseSink *sink,
+ const pa_sink_input_info *info);
-gboolean pulse_sink_update (PulseStream *pstream,
- const pa_sink_info *info,
- PulseDevice *device);
+void pulse_sink_remove_input (PulseSink *sink, guint32 index);
+
+void pulse_sink_update (PulseSink *sink,
+ const pa_sink_info *info);
+
+guint32 pulse_sink_get_index_monitor (PulseSink *sink);
G_END_DECLS
diff --git a/backends/pulse/pulse-source-control.c b/backends/pulse/pulse-source-control.c
new file mode 100644
index 0000000..3ed1573
--- /dev/null
+++ b/backends/pulse/pulse-source-control.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-monitor.h"
+#include "pulse-stream-control.h"
+#include "pulse-source.h"
+#include "pulse-source-control.h"
+
+static void pulse_source_control_class_init (PulseSourceControlClass *klass);
+static void pulse_source_control_init (PulseSourceControl *control);
+
+G_DEFINE_TYPE (PulseSourceControl, pulse_source_control, PULSE_TYPE_STREAM_CONTROL);
+
+static gboolean pulse_source_control_set_mute (PulseStreamControl *psc,
+ gboolean mute);
+static gboolean pulse_source_control_set_volume (PulseStreamControl *psc,
+ pa_cvolume *cvolume);
+static PulseMonitor *pulse_source_control_create_monitor (PulseStreamControl *psc);
+
+static void
+pulse_source_control_class_init (PulseSourceControlClass *klass)
+{
+ PulseStreamControlClass *control_class;
+
+ control_class = PULSE_STREAM_CONTROL_CLASS (klass);
+ control_class->set_mute = pulse_source_control_set_mute;
+ control_class->set_volume = pulse_source_control_set_volume;
+ control_class->create_monitor = pulse_source_control_create_monitor;
+}
+
+static void
+pulse_source_control_init (PulseSourceControl *control)
+{
+}
+
+PulseSourceControl *
+pulse_source_control_new (PulseSource *source,
+ const pa_source_info *info)
+{
+ PulseSourceControl *control;
+ MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
+ MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_MONITOR;
+ MateMixerStreamControlRole role;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE (source), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (info->active_port != NULL)
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_PORT;
+ else
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_MASTER;
+
+ /* Build the flag list */
+ if (info->flags & PA_SOURCE_DECIBEL_VOLUME)
+ flags |= MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
+
+ control = g_object_new (PULSE_TYPE_SOURCE_CONTROL,
+ "name", info->name,
+ "label", info->description,
+ "flags", flags,
+ "role", role,
+ "stream", source,
+ NULL);
+
+ pulse_source_control_update (control, info);
+ return control;
+}
+
+void
+pulse_source_control_update (PulseSourceControl *control, const pa_source_info *info)
+{
+ g_return_if_fail (PULSE_IS_SOURCE_CONTROL (control));
+ g_return_if_fail (info != NULL);
+
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (control));
+
+ _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (control),
+ info->mute ? TRUE : FALSE);
+
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (control),
+ &info->channel_map);
+
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (control),
+ &info->volume,
+ info->base_volume);
+
+ g_object_thaw_notify (G_OBJECT (control));
+}
+
+static gboolean
+pulse_source_control_set_mute (PulseStreamControl *psc, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), FALSE);
+
+ return pulse_connection_set_source_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc),
+ mute);
+}
+
+static gboolean
+pulse_source_control_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume)
+{
+ g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), FALSE);
+ g_return_val_if_fail (cvolume != NULL, FALSE);
+
+ return pulse_connection_set_source_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc),
+ cvolume);
+}
+
+static PulseMonitor *
+pulse_source_control_create_monitor (PulseStreamControl *psc)
+{
+ guint32 index;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE_CONTROL (psc), NULL);
+
+ index = PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc);
+ if G_UNLIKELY (index == PA_INVALID_INDEX) {
+ g_debug ("Monitor of stream control %s is not available",
+ mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (psc)));
+ return NULL;
+ }
+
+ return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ index,
+ PA_INVALID_INDEX);
+}
diff --git a/backends/pulse/pulse-source-control.h b/backends/pulse/pulse-source-control.h
new file mode 100644
index 0000000..a8d659f
--- /dev/null
+++ b/backends/pulse/pulse-source-control.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_SOURCE_CONTROL_H
+#define PULSE_SOURCE_CONTROL_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-stream-control.h"
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_SOURCE_CONTROL \
+ (pulse_source_control_get_type ())
+#define PULSE_SOURCE_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControl))
+#define PULSE_IS_SOURCE_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_CONTROL))
+#define PULSE_SOURCE_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControlClass))
+#define PULSE_IS_SOURCE_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_CONTROL))
+#define PULSE_SOURCE_CONTROL_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_CONTROL, PulseSourceControlClass))
+
+typedef struct _PulseSourceControlClass PulseSourceControlClass;
+typedef struct _PulseSourceControlPrivate PulseSourceControlPrivate;
+
+struct _PulseSourceControl
+{
+ PulseStreamControl parent;
+};
+
+struct _PulseSourceControlClass
+{
+ PulseStreamControlClass parent_class;
+};
+
+GType pulse_source_control_get_type (void) G_GNUC_CONST;
+
+PulseSourceControl *pulse_source_control_new (PulseSource *source,
+ const pa_source_info *info);
+
+void pulse_source_control_update (PulseSourceControl *control,
+ const pa_source_info *info);
+
+G_END_DECLS
+
+#endif /* PULSE_SOURCE_CONTROL_H */
diff --git a/backends/pulse/pulse-source-output.c b/backends/pulse/pulse-source-output.c
index 6cbd888..69fc3e4 100644
--- a/backends/pulse/pulse-source-output.c
+++ b/backends/pulse/pulse-source-output.c
@@ -17,56 +17,39 @@
#include <glib.h>
#include <glib-object.h>
-#include <string.h>
-
-#include <libmatemixer/matemixer-client-stream.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
-#include "pulse-client-stream.h"
#include "pulse-helpers.h"
#include "pulse-monitor.h"
-#include "pulse-stream.h"
#include "pulse-source.h"
#include "pulse-source-output.h"
+#include "pulse-stream.h"
+#include "pulse-stream-control.h"
static void pulse_source_output_class_init (PulseSourceOutputClass *klass);
static void pulse_source_output_init (PulseSourceOutput *output);
-G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_CLIENT_STREAM);
-
-static void pulse_source_output_reload (PulseStream *pstream);
-
-static gboolean pulse_source_output_set_mute (PulseStream *pstream,
- gboolean mute);
-static gboolean pulse_source_output_set_volume (PulseStream *pstream,
- pa_cvolume *cvolume);
+G_DEFINE_TYPE (PulseSourceOutput, pulse_source_output, PULSE_TYPE_STREAM_CONTROL);
-static gboolean pulse_source_output_set_parent (PulseClientStream *pclient,
- PulseStream *parent);
-static gboolean pulse_source_output_remove (PulseClientStream *pclient);
-
-static PulseMonitor *pulse_source_output_create_monitor (PulseStream *pstream);
+static gboolean pulse_source_output_set_mute (PulseStreamControl *psc,
+ gboolean mute);
+static gboolean pulse_source_output_set_volume (PulseStreamControl *psc,
+ pa_cvolume *cvolume);
+static PulseMonitor *pulse_source_output_create_monitor (PulseStreamControl *psc);
static void
pulse_source_output_class_init (PulseSourceOutputClass *klass)
{
- PulseStreamClass *stream_class;
- PulseClientStreamClass *client_class;
-
- stream_class = PULSE_STREAM_CLASS (klass);
-
- stream_class->reload = pulse_source_output_reload;
- stream_class->set_mute = pulse_source_output_set_mute;
- stream_class->set_volume = pulse_source_output_set_volume;
- stream_class->create_monitor = pulse_source_output_create_monitor;
-
- client_class = PULSE_CLIENT_STREAM_CLASS (klass);
+ PulseStreamControlClass *control_class;
- client_class->set_parent = pulse_source_output_set_parent;
- client_class->remove = pulse_source_output_remove;
+ control_class = PULSE_STREAM_CONTROL_CLASS (klass);
+ control_class->set_mute = pulse_source_output_set_mute;
+ control_class->set_volume = pulse_source_output_set_volume;
+ control_class->create_monitor = pulse_source_output_create_monitor;
}
static void
@@ -74,210 +57,157 @@ pulse_source_output_init (PulseSourceOutput *output)
{
}
-PulseStream *
-pulse_source_output_new (PulseConnection *connection,
- const pa_source_output_info *info,
- PulseStream *parent)
+PulseSourceOutput *
+pulse_source_output_new (PulseSource *source,
+ const pa_source_output_info *info)
{
PulseSourceOutput *output;
+ gchar *name;
+ const gchar *prop;
+ MateMixerAppInfo *app_info = NULL;
- g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
- g_return_val_if_fail (info != NULL, NULL);
-
- /* Consider the sink input index as unchanging parameter */
- output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT,
- "connection", connection,
- "index", info->index,
- NULL);
-
- /* Other data may change at any time, so let's make a use of our update function */
- pulse_source_output_update (PULSE_STREAM (output), info, parent);
+ MateMixerStreamControlFlags flags = MATE_MIXER_STREAM_CONTROL_MUTE_READABLE |
+ MATE_MIXER_STREAM_CONTROL_MUTE_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_MONITOR;
+ MateMixerStreamControlRole role = MATE_MIXER_STREAM_CONTROL_ROLE_UNKNOWN;
- return PULSE_STREAM (output);
-}
+ MateMixerStreamControlMediaRole media_role = MATE_MIXER_STREAM_CONTROL_MEDIA_ROLE_UNKNOWN;
-gboolean
-pulse_source_output_update (PulseStream *pstream,
- const pa_source_output_info *info,
- PulseStream *parent)
-{
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
- MATE_MIXER_STREAM_CLIENT;
- PulseClientStream *pclient;
- const gchar *prop;
- const gchar *description = NULL;
- gchar *name;
+ g_return_val_if_fail (PULSE_IS_SOURCE (source), NULL);
+ g_return_val_if_fail (info != NULL, NULL);
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
+ /* Many mixer applications query the Pulse client list and use the client
+ * name here, but we use the name only as an identifier, so let's avoid
+ * this unnecessary overhead and use a custom name.
+ * Also make sure to make the name unique by including the PulseAudio index. */
+ name = g_strdup_printf ("pulse-input-control-%lu", (gulong) info->index);
- pclient = PULSE_CLIENT_STREAM (pstream);
+#if PA_CHECK_VERSION(1, 0, 0)
+ if (info->has_volume) {
+ flags |=
+ MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
- /* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (pstream));
+ if (info->volume_writable)
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE;
+ }
+#else
+ /* Pre-1.0 PulseAudio does not include the has_volume and volume_writable
+ * fields, but does include the volume info, so let's give it a try */
+ flags |=
+ MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL;
+#endif
- /* Many other mixer applications query the Pulse client list and use the
- * client name here, but we use the name only as an identifier, so let's avoid
- * this unnecessary overhead and use a custom name.
- * Also make sure to make the name unique by including the Pulse index. */
- name = g_strdup_printf ("pulse-stream-client-input-%lu", (gulong) info->index);
+ if (info->client != PA_INVALID_INDEX) {
+ app_info = _mate_mixer_app_info_new ();
- pulse_stream_update_name (pstream, name);
- g_free (name);
+ role = MATE_MIXER_STREAM_CONTROL_ROLE_APPLICATION;
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
- if (prop != NULL)
- pulse_client_stream_update_app_name (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_NAME);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_name (app_info, prop);
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
- if (prop != NULL)
- pulse_client_stream_update_app_id (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ID);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_id (app_info, prop);
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
- if (prop != NULL)
- pulse_client_stream_update_app_version (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_VERSION);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_version (app_info, prop);
- prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
- if (prop != NULL)
- pulse_client_stream_update_app_icon (pclient, prop);
+ prop = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_ICON_NAME);
+ if (prop != NULL)
+ _mate_mixer_app_info_set_icon (app_info, prop);
+ }
prop = pa_proplist_gets (info->proplist, PA_PROP_MEDIA_ROLE);
- if (prop != NULL) {
- MateMixerClientStreamRole role = pulse_convert_media_role_name (prop);
+ if (prop != NULL)
+ media_role = pulse_convert_media_role_name (prop);
- if (role == MATE_MIXER_CLIENT_STREAM_ROLE_EVENT) {
- /* The event description seems to provide much better readable
- * description for event streams */
- prop = pa_proplist_gets (info->proplist, PA_PROP_EVENT_DESCRIPTION);
+ output = g_object_new (PULSE_TYPE_SOURCE_OUTPUT,
+ "name", name,
+ "label", info->name,
+ "flags", flags,
+ "role", role,
+ "media-role", media_role,
+ "index", info->index,
+ "stream", source,
+ NULL);
+ g_free (name);
- if (G_LIKELY (prop != NULL))
- description = prop;
- }
- pulse_client_stream_update_role (pclient, role);
- } else
- pulse_client_stream_update_role (pclient, MATE_MIXER_CLIENT_STREAM_ROLE_NONE);
+ if (app_info != NULL)
+ pulse_stream_control_set_app_info (PULSE_STREAM_CONTROL (output), app_info);
- if (description == NULL)
- description = info->name;
+ pulse_source_output_update (output, info);
+ return output;
+}
- pulse_stream_update_description (pstream, description);
+void
+pulse_source_output_update (PulseSourceOutput *output,
+ const pa_source_output_info *info)
+{
+ g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (output));
+ g_return_if_fail (info != NULL);
- if (info->client != PA_INVALID_INDEX)
- pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_APPLICATION);
- else
- pulse_client_stream_update_flags (pclient, MATE_MIXER_CLIENT_STREAM_NO_FLAGS);
+ /* Let all the information update before emitting notify signals */
+ g_object_freeze_notify (G_OBJECT (output));
- if (G_LIKELY (parent != NULL)) {
- pulse_client_stream_update_parent (pclient, MATE_MIXER_STREAM (parent));
- flags |= MATE_MIXER_STREAM_HAS_MONITOR;
- } else
- pulse_client_stream_update_parent (pclient, NULL);
+ _mate_mixer_stream_control_set_mute (MATE_MIXER_STREAM_CONTROL (output),
+ info->mute ? TRUE : FALSE);
#if PA_CHECK_VERSION(1, 0, 0)
- if (info->has_volume) {
- flags |= MATE_MIXER_STREAM_HAS_VOLUME;
-
- if (info->volume_writable)
- flags |= MATE_MIXER_STREAM_CAN_SET_VOLUME;
- }
-
- flags |= MATE_MIXER_STREAM_HAS_MUTE;
-
- /* Flags needed before volume */
- pulse_stream_update_flags (pstream, flags);
- pulse_stream_update_channel_map (pstream, &info->channel_map);
- pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output),
+ &info->channel_map);
if (info->has_volume)
- pulse_stream_update_volume (pstream, &info->volume, 0);
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
+ &info->volume,
+ 0);
else
- pulse_stream_update_volume (pstream, NULL, 0);
+ pulse_stream_control_set_cvolume (PULSE_STREAM_CONTROL (output),
+ NULL,
+ 0);
#else
- /* Flags needed before volume */
- pulse_stream_update_flags (pstream, flags);
+ pulse_stream_control_set_channel_map (PULSE_STREAM_CONTROL (output),
+ &info->channel_map);
- pulse_stream_update_channel_map (pstream, &info->channel_map);
- pulse_stream_update_volume (pstream, NULL, 0);
+ pulse_stream_control_set_volume (PULSE_STREAM_CONTROL (output),
+ &info->volume,
+ 0);
#endif
- // XXX needs to fix monitor if parent changes
-
- g_object_thaw_notify (G_OBJECT (pstream));
- return TRUE;
-}
-
-static void
-pulse_source_output_reload (PulseStream *pstream)
-{
- g_return_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream));
-
- pulse_connection_load_source_output_info (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream));
+ g_object_thaw_notify (G_OBJECT (output));
}
static gboolean
-pulse_source_output_set_mute (PulseStream *pstream, gboolean mute)
+pulse_source_output_set_mute (PulseStreamControl *psc, gboolean mute)
{
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE);
- return pulse_connection_set_source_output_mute (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
+ return pulse_connection_set_source_output_mute (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ pulse_stream_control_get_index (psc),
mute);
}
static gboolean
-pulse_source_output_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
+pulse_source_output_set_volume (PulseStreamControl *psc, pa_cvolume *cvolume)
{
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), FALSE);
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), FALSE);
g_return_val_if_fail (cvolume != NULL, FALSE);
- return pulse_connection_set_source_output_volume (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
+ return pulse_connection_set_source_output_volume (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ pulse_stream_control_get_index (psc),
cvolume);
}
-static gboolean
-pulse_source_output_set_parent (PulseClientStream *pclient, PulseStream *parent)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE);
-
- pstream = PULSE_STREAM (pclient);
-
- return pulse_connection_move_sink_input (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- pulse_stream_get_index (parent));
-}
-
-static gboolean
-pulse_source_output_remove (PulseClientStream *pclient)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pclient), FALSE);
-
- pstream = PULSE_STREAM (pclient);
-
- return pulse_connection_kill_source_output (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream));
-}
-
static PulseMonitor *
-pulse_source_output_create_monitor (PulseStream *pstream)
+pulse_source_output_create_monitor (PulseStreamControl *psc)
{
- MateMixerStream *parent;
-
- g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (pstream), NULL);
-
- parent = mate_mixer_client_stream_get_parent (MATE_MIXER_CLIENT_STREAM (pstream));
- if (G_UNLIKELY (parent == NULL)) {
- g_debug ("Not creating monitor for client stream %s: not available",
- mate_mixer_stream_get_name (MATE_MIXER_STREAM (pstream)));
- return NULL;
- }
+ g_return_val_if_fail (PULSE_IS_SOURCE_OUTPUT (psc), NULL);
- return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (PULSE_STREAM (parent)),
+ return pulse_connection_create_monitor (PULSE_STREAM_CONTROL_GET_CONNECTION (psc),
+ PULSE_STREAM_CONTROL_GET_STREAM_INDEX (psc),
PA_INVALID_INDEX);
}
diff --git a/backends/pulse/pulse-source-output.h b/backends/pulse/pulse-source-output.h
index 845d439..1037f94 100644
--- a/backends/pulse/pulse-source-output.h
+++ b/backends/pulse/pulse-source-output.h
@@ -23,48 +23,43 @@
#include <pulse/pulseaudio.h>
-#include "pulse-client-stream.h"
-#include "pulse-connection.h"
-#include "pulse-stream.h"
+#include "pulse-stream-control.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
-#define PULSE_TYPE_SOURCE_OUTPUT \
+#define PULSE_TYPE_SOURCE_OUTPUT \
(pulse_source_output_get_type ())
-#define PULSE_SOURCE_OUTPUT(o) \
+#define PULSE_SOURCE_OUTPUT(o) \
(G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutput))
-#define PULSE_IS_SOURCE_OUTPUT(o) \
+#define PULSE_IS_SOURCE_OUTPUT(o) \
(G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_OUTPUT))
-#define PULSE_SOURCE_OUTPUT_CLASS(k) \
+#define PULSE_SOURCE_OUTPUT_CLASS(k) \
(G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass))
-#define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \
+#define PULSE_IS_SOURCE_OUTPUT_CLASS(k) \
(G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_OUTPUT))
-#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \
+#define PULSE_SOURCE_OUTPUT_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_OUTPUT, PulseSourceOutputClass))
-typedef struct _PulseSourceOutput PulseSourceOutput;
-typedef struct _PulseSourceOutputClass PulseSourceOutputClass;
-typedef struct _PulseSourceOutputPrivate PulseSourceOutputPrivate;
+typedef struct _PulseSourceOutputClass PulseSourceOutputClass;
struct _PulseSourceOutput
{
- PulseClientStream parent;
+ PulseStreamControl parent;
};
struct _PulseSourceOutputClass
{
- PulseClientStreamClass parent_class;
+ PulseStreamControlClass parent_class;
};
-GType pulse_source_output_get_type (void) G_GNUC_CONST;
+GType pulse_source_output_get_type (void) G_GNUC_CONST;
-PulseStream *pulse_source_output_new (PulseConnection *connection,
- const pa_source_output_info *info,
- PulseStream *parent);
+PulseSourceOutput *pulse_source_output_new (PulseSource *source,
+ const pa_source_output_info *info);
-gboolean pulse_source_output_update (PulseStream *pstream,
- const pa_source_output_info *info,
- PulseStream *parent);
+void pulse_source_output_update (PulseSourceOutput *output,
+ const pa_source_output_info *info);
G_END_DECLS
diff --git a/backends/pulse/pulse-source-switch.c b/backends/pulse/pulse-source-switch.c
new file mode 100644
index 0000000..178702e
--- /dev/null
+++ b/backends/pulse/pulse-source-switch.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include "pulse-connection.h"
+#include "pulse-port.h"
+#include "pulse-port-switch.h"
+#include "pulse-source-switch.h"
+#include "pulse-stream.h"
+
+static void pulse_source_switch_class_init (PulseSourceSwitchClass *klass);
+static void pulse_source_switch_init (PulseSourceSwitch *swtch);
+
+G_DEFINE_TYPE (PulseSourceSwitch, pulse_source_switch, PULSE_TYPE_PORT_SWITCH)
+
+static gboolean pulse_source_switch_set_active_port (PulsePortSwitch *swtch,
+ PulsePort *port);
+
+static void
+pulse_source_switch_class_init (PulseSourceSwitchClass *klass)
+{
+ PulsePortSwitchClass *switch_class;
+
+ switch_class = PULSE_PORT_SWITCH_CLASS (klass);
+ switch_class->set_active_port = pulse_source_switch_set_active_port;
+}
+
+static void
+pulse_source_switch_init (PulseSourceSwitch *swtch)
+{
+}
+
+PulsePortSwitch *
+pulse_source_switch_new (const gchar *name, const gchar *label, PulseSource *source)
+{
+ return g_object_new (PULSE_TYPE_SOURCE_SWITCH,
+ "name", name,
+ "label", label,
+ "role", MATE_MIXER_SWITCH_ROLE_PORT,
+ "stream", source,
+ NULL);
+}
+
+static gboolean
+pulse_source_switch_set_active_port (PulsePortSwitch *swtch, PulsePort *port)
+{
+ PulseStream *stream;
+
+ g_return_val_if_fail (PULSE_IS_SOURCE_SWITCH (swtch), FALSE);
+ g_return_val_if_fail (PULSE_IS_PORT (port), FALSE);
+
+ stream = pulse_port_switch_get_stream (swtch);
+
+ return pulse_connection_set_source_port (pulse_stream_get_connection (stream),
+ pulse_stream_get_index (stream),
+ pulse_port_get_name (port));
+}
diff --git a/backends/pulse/pulse-source-switch.h b/backends/pulse/pulse-source-switch.h
new file mode 100644
index 0000000..408e872
--- /dev/null
+++ b/backends/pulse/pulse-source-switch.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_SOURCE_SWITCH_H
+#define PULSE_SOURCE_SWITCH_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_SOURCE_SWITCH \
+ (pulse_source_switch_get_type ())
+#define PULSE_SOURCE_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitch))
+#define PULSE_IS_SOURCE_SWITCH(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_SOURCE_SWITCH))
+#define PULSE_SOURCE_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitchClass))
+#define PULSE_IS_SOURCE_SWITCH_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_SOURCE_SWITCH))
+#define PULSE_SOURCE_SWITCH_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE_SWITCH, PulseSourceSwitchClass))
+
+typedef struct _PulseSourceSwitchClass PulseSourceSwitchClass;
+typedef struct _PulseSourceSwitchPrivate PulseSourceSwitchPrivate;
+
+struct _PulseSourceSwitch
+{
+ PulsePortSwitch parent;
+};
+
+struct _PulseSourceSwitchClass
+{
+ PulsePortSwitchClass parent_class;
+};
+
+GType pulse_source_switch_get_type (void) G_GNUC_CONST;
+
+PulsePortSwitch *pulse_source_switch_new (const gchar *name,
+ const gchar *label,
+ PulseSource *source);
+
+G_END_DECLS
+
+#endif /* PULSE_SOURCE_SWITCH_H */
diff --git a/backends/pulse/pulse-source.c b/backends/pulse/pulse-source.c
index e7dce6f..0d095a7 100644
--- a/backends/pulse/pulse-source.c
+++ b/backends/pulse/pulse-source.c
@@ -16,253 +16,238 @@
*/
#include <glib.h>
+#include <glib/gi18n.h>
#include <glib-object.h>
-
-#include <libmatemixer/matemixer-port.h>
-#include <libmatemixer/matemixer-port-private.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
#include "pulse-device.h"
#include "pulse-monitor.h"
+#include "pulse-port.h"
+#include "pulse-port-switch.h"
#include "pulse-stream.h"
#include "pulse-source.h"
+#include "pulse-source-control.h"
+#include "pulse-source-output.h"
+#include "pulse-source-switch.h"
+
+struct _PulseSourcePrivate
+{
+ GHashTable *outputs;
+ PulsePortSwitch *pswitch;
+ PulseSourceControl *control;
+};
static void pulse_source_class_init (PulseSourceClass *klass);
static void pulse_source_init (PulseSource *source);
+static void pulse_source_dispose (GObject *object);
+static void pulse_source_finalize (GObject *object);
G_DEFINE_TYPE (PulseSource, pulse_source, PULSE_TYPE_STREAM);
-static void pulse_source_reload (PulseStream *pstream);
+static const GList *pulse_source_list_controls (MateMixerStream *mms);
+static const GList *pulse_source_list_switches (MateMixerStream *mms);
-static gboolean pulse_source_set_mute (PulseStream *pstream,
- gboolean mute);
-static gboolean pulse_source_set_volume (PulseStream *pstream,
- pa_cvolume *cvolume);
-static gboolean pulse_source_set_active_port (PulseStream *pstream,
- MateMixerPort *port);
+static void
+pulse_source_class_init (PulseSourceClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerStreamClass *stream_class;
-static PulseMonitor *pulse_source_create_monitor (PulseStream *pstream);
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_source_dispose;
+ object_class->finalize = pulse_source_finalize;
-static void update_ports (PulseStream *pstream,
- pa_source_port_info **ports,
- pa_source_port_info *active);
+ stream_class = MATE_MIXER_STREAM_CLASS (klass);
+ stream_class->list_controls = pulse_source_list_controls;
+ stream_class->list_switches = pulse_source_list_switches;
+
+ g_type_class_add_private (klass, sizeof (PulseSourcePrivate));
+}
static void
-pulse_source_class_init (PulseSourceClass *klass)
+pulse_source_init (PulseSource *source)
+{
+ source->priv = G_TYPE_INSTANCE_GET_PRIVATE (source,
+ PULSE_TYPE_SOURCE,
+ PulseSourcePrivate);
+
+ source->priv->outputs = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ g_object_unref);
+}
+
+static void
+pulse_source_dispose (GObject *object)
{
- PulseStreamClass *stream_class;
+ PulseSource *source;
- stream_class = PULSE_STREAM_CLASS (klass);
+ source = PULSE_SOURCE (object);
- stream_class->reload = pulse_source_reload;
- stream_class->set_mute = pulse_source_set_mute;
- stream_class->set_volume = pulse_source_set_volume;
- stream_class->set_active_port = pulse_source_set_active_port;
- stream_class->create_monitor = pulse_source_create_monitor;
+ g_clear_object (&source->priv->control);
+ g_clear_object (&source->priv->pswitch);
+
+ g_hash_table_remove_all (source->priv->outputs);
+
+ G_OBJECT_CLASS (pulse_source_parent_class)->dispose (object);
}
static void
-pulse_source_init (PulseSource *source)
+pulse_source_finalize (GObject *object)
{
+ PulseSource *source;
+
+ source = PULSE_SOURCE (object);
+
+ g_hash_table_unref (source->priv->outputs);
+
+ G_OBJECT_CLASS (pulse_source_parent_class)->finalize (object);
}
-PulseStream *
+PulseSource *
pulse_source_new (PulseConnection *connection,
const pa_source_info *info,
PulseDevice *device)
{
- PulseStream *stream;
+ PulseSource *source;
g_return_val_if_fail (PULSE_IS_CONNECTION (connection), NULL);
g_return_val_if_fail (info != NULL, NULL);
- /* Consider the sink index as unchanging parameter */
- stream = g_object_new (PULSE_TYPE_SOURCE,
+ source = g_object_new (PULSE_TYPE_SOURCE,
+ "name", info->name,
+ "label", info->description,
+ "device", device,
+ "direction", MATE_MIXER_DIRECTION_INPUT,
"connection", connection,
"index", info->index,
NULL);
- /* Other data may change at any time, so let's make a use of our update function */
- pulse_source_update (stream, info, device);
+ source->priv->control = pulse_source_control_new (source, info);
- return stream;
-}
+ if (info->n_ports > 0) {
+ pa_source_port_info **ports = info->ports;
-gboolean
-pulse_source_update (PulseStream *pstream,
- const pa_source_info *info,
- PulseDevice *device)
-{
- MateMixerStreamFlags flags = MATE_MIXER_STREAM_INPUT |
- MATE_MIXER_STREAM_HAS_MUTE |
- MATE_MIXER_STREAM_HAS_MONITOR |
- MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_CAN_SET_VOLUME |
- MATE_MIXER_STREAM_CAN_SUSPEND;
-
- g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
- g_return_val_if_fail (info != NULL, FALSE);
-
- /* Let all the information update before emitting notify signals */
- g_object_freeze_notify (G_OBJECT (pstream));
-
- pulse_stream_update_name (pstream, info->name);
- pulse_stream_update_description (pstream, info->description);
- pulse_stream_update_mute (pstream, info->mute ? TRUE : FALSE);
-
- /* Stream state */
- switch (info->state) {
- case PA_SOURCE_RUNNING:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_RUNNING);
- break;
- case PA_SOURCE_IDLE:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE);
- break;
- case PA_SOURCE_SUSPENDED:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED);
- break;
- default:
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_UNKNOWN);
- break;
- }
+ /* Create the port switch */
+ source->priv->pswitch = pulse_source_switch_new ("port", _("Port"), source);
+
+ while (*ports != NULL) {
+ pa_source_port_info *p = *ports++;
+ PulsePort *port;
+ const gchar *icon = NULL;
- /* Build the flag list */
- if (info->flags & PA_SINK_DECIBEL_VOLUME)
- flags |= MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME;
- if (info->flags & PA_SINK_FLAT_VOLUME)
- flags |= MATE_MIXER_STREAM_HAS_FLAT_VOLUME;
+ /* A port may include an icon but in PulseAudio sink and source ports
+ * the property is not included, for this reason ports are also read from
+ * devices where the icons may be present */
+ if (device != NULL) {
+ port = pulse_device_get_port (PULSE_DEVICE (device), p->name);
+ if (port != NULL)
+ icon = mate_mixer_switch_option_get_icon (MATE_MIXER_SWITCH_OPTION (port));
+ }
- /* Flags must be updated before volume */
- pulse_stream_update_flags (pstream, flags);
+ port = pulse_port_new (p->name,
+ p->description,
+ icon,
+ p->priority);
- pulse_stream_update_channel_map (pstream, &info->channel_map);
- pulse_stream_update_volume (pstream, &info->volume, info->base_volume);
+ pulse_port_switch_add_port (source->priv->pswitch, port);
- pulse_stream_update_device (pstream, MATE_MIXER_DEVICE (device));
+ if (p == info->active_port)
+ pulse_port_switch_set_active_port (source->priv->pswitch, port);
+ }
- /* Ports must be updated after device */
- if (info->ports != NULL) {
- update_ports (pstream, info->ports, info->active_port);
+ g_debug ("Created port list for source %s", info->name);
}
- g_object_thaw_notify (G_OBJECT (pstream));
- return TRUE;
-}
+ pulse_source_update (source, info);
-static void
-pulse_source_reload (PulseStream *pstream)
-{
- g_return_if_fail (PULSE_IS_SOURCE (pstream));
-
- pulse_connection_load_source_info (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream));
+ _mate_mixer_stream_set_default_control (MATE_MIXER_STREAM (source),
+ MATE_MIXER_STREAM_CONTROL (source->priv->control));
+ return source;
}
-static gboolean
-pulse_source_set_mute (PulseStream *pstream, gboolean mute)
+void
+pulse_source_add_output (PulseSource *source, const pa_source_output_info *info)
{
- g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
-
- return pulse_connection_set_source_mute (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- mute);
+ PulseSourceOutput *output;
+
+ /* This function is used for both creating and refreshing source outputs */
+ output = g_hash_table_lookup (source->priv->outputs, GINT_TO_POINTER (info->index));
+ if (output == NULL) {
+ const gchar *name;
+
+ output = pulse_source_output_new (source, info);
+ g_hash_table_insert (source->priv->outputs,
+ GINT_TO_POINTER (info->index),
+ output);
+
+ name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (output));
+ g_signal_emit_by_name (G_OBJECT (source),
+ "control-added",
+ name);
+ } else
+ pulse_source_output_update (output, info);
}
-static gboolean
-pulse_source_set_volume (PulseStream *pstream, pa_cvolume *cvolume)
+void
+pulse_source_remove_output (PulseSource *source, guint32 index)
{
- g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
- g_return_val_if_fail (cvolume != NULL, FALSE);
+ PulseSourceOutput *output;
+ const gchar *name;
- return pulse_connection_set_source_volume (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- cvolume);
-}
+ output = g_hash_table_lookup (source->priv->outputs, GINT_TO_POINTER (index));
+ if G_UNLIKELY (output == NULL)
+ return;
-static gboolean
-pulse_source_set_active_port (PulseStream *pstream, MateMixerPort *port)
-{
- g_return_val_if_fail (PULSE_IS_SOURCE (pstream), FALSE);
- g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
+ name = mate_mixer_stream_control_get_name (MATE_MIXER_STREAM_CONTROL (output));
- return pulse_connection_set_source_port (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- mate_mixer_port_get_name (port));
+ g_hash_table_remove (source->priv->outputs, GINT_TO_POINTER (index));
+ g_signal_emit_by_name (G_OBJECT (source),
+ "control-removed",
+ name);
}
-static PulseMonitor *
-pulse_source_create_monitor (PulseStream *pstream)
+void
+pulse_source_update (PulseSource *source,
+ const pa_source_info *info)
{
- g_return_val_if_fail (PULSE_IS_SOURCE (pstream), NULL);
-
- return pulse_connection_create_monitor (pulse_stream_get_connection (pstream),
- pulse_stream_get_index (pstream),
- PA_INVALID_INDEX);
+ g_return_if_fail (PULSE_IS_SOURCE (source));
+ g_return_if_fail (info != NULL);
+
+ /* The switch doesn't allow being unset, PulseAudio should always include
+ * the active port name if the are any ports available */
+ if (info->active_port != NULL)
+ pulse_port_switch_set_active_port_by_name (source->priv->pswitch,
+ info->active_port->name);
}
-static void
-update_ports (PulseStream *pstream,
- pa_source_port_info **ports,
- pa_source_port_info *active)
+static const GList *
+pulse_source_list_controls (MateMixerStream *mms)
{
- MateMixerPort *port;
- MateMixerDevice *device;
- GHashTable *hash;
-
- hash = pulse_stream_get_ports (pstream);
+ GList *list;
- while (*ports != NULL) {
- MateMixerPortFlags flags = MATE_MIXER_PORT_NO_FLAGS;
- pa_source_port_info *info = *ports;
- const gchar *icon = NULL;
+ g_return_val_if_fail (PULSE_IS_SOURCE (mms), NULL);
- device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (pstream));
- if (device != NULL) {
- port = mate_mixer_device_get_port (device, info->name);
-
- if (port != NULL) {
- flags = mate_mixer_port_get_flags (port);
- icon = mate_mixer_port_get_icon (port);
- }
- }
+ // XXX
+ list = g_hash_table_get_values (PULSE_SOURCE (mms)->priv->outputs);
+ if (list != NULL)
+ g_list_foreach (list, (GFunc) g_object_ref, NULL);
-#if PA_CHECK_VERSION(2, 0, 0)
- if (info->available == PA_PORT_AVAILABLE_YES)
- flags |= MATE_MIXER_PORT_AVAILABLE;
- else
- flags &= ~MATE_MIXER_PORT_AVAILABLE;
-#endif
-
- port = g_hash_table_lookup (hash, info->name);
-
- if (port != NULL) {
- /* Update existing port */
- _mate_mixer_port_update_description (port, info->description);
- _mate_mixer_port_update_icon (port, icon);
- _mate_mixer_port_update_priority (port, info->priority);
- _mate_mixer_port_update_flags (port, flags);
- } else {
- /* Add previously unknown port to the hash table */
- port = _mate_mixer_port_new (info->name,
- info->description,
- icon,
- info->priority,
- flags);
-
- g_hash_table_insert (hash, g_strdup (info->name), port);
- }
+ return g_list_prepend (list, g_object_ref (PULSE_SOURCE (mms)->priv->control));
+}
- ports++;
- }
+static const GList *
+pulse_source_list_switches (MateMixerStream *mms)
+{
+ g_return_val_if_fail (PULSE_IS_SOURCE (mms), NULL);
- /* Active port */
- if (G_LIKELY (active != NULL))
- port = g_hash_table_lookup (hash, active->name);
- else
- port = NULL;
+ // XXX
+ if (PULSE_SOURCE (mms)->priv->pswitch != NULL)
+ return g_list_prepend (NULL, PULSE_SOURCE (mms)->priv->pswitch);
- pulse_stream_update_active_port (pstream, port);
+ return NULL;
}
diff --git a/backends/pulse/pulse-source.h b/backends/pulse/pulse-source.h
index 9abf6d8..fdc3d5e 100644
--- a/backends/pulse/pulse-source.h
+++ b/backends/pulse/pulse-source.h
@@ -23,9 +23,8 @@
#include <pulse/pulseaudio.h>
-#include "pulse-connection.h"
-#include "pulse-device.h"
#include "pulse-stream.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
@@ -42,12 +41,15 @@ G_BEGIN_DECLS
#define PULSE_SOURCE_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_SOURCE, PulseSourceClass))
-typedef struct _PulseSource PulseSource;
typedef struct _PulseSourceClass PulseSourceClass;
+typedef struct _PulseSourcePrivate PulseSourcePrivate;
struct _PulseSource
{
PulseStream parent;
+
+ /*< private >*/
+ PulseSourcePrivate *priv;
};
struct _PulseSourceClass
@@ -55,15 +57,20 @@ struct _PulseSourceClass
PulseStreamClass parent_class;
};
-GType pulse_source_get_type (void) G_GNUC_CONST;
+GType pulse_source_get_type (void) G_GNUC_CONST;
+
+PulseSource *pulse_source_new (PulseConnection *connection,
+ const pa_source_info *info,
+ PulseDevice *device);
+
+void pulse_source_add_output (PulseSource *source,
+ const pa_source_output_info *info);
-PulseStream *pulse_source_new (PulseConnection *connection,
- const pa_source_info *info,
- PulseDevice *device);
+void pulse_source_remove_output (PulseSource *source,
+ guint32 index);
-gboolean pulse_source_update (PulseStream *pstream,
- const pa_source_info *info,
- PulseDevice *device);
+void pulse_source_update (PulseSource *source,
+ const pa_source_info *info);
G_END_DECLS
diff --git a/backends/pulse/pulse-stream-control.c b/backends/pulse/pulse-stream-control.c
new file mode 100644
index 0000000..fa17e6b
--- /dev/null
+++ b/backends/pulse/pulse-stream-control.c
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-connection.h"
+#include "pulse-helpers.h"
+#include "pulse-monitor.h"
+#include "pulse-stream-control.h"
+
+struct _PulseStreamControlPrivate
+{
+ guint32 index;
+ guint volume;
+ pa_cvolume cvolume;
+ pa_volume_t base_volume;
+ pa_channel_map channel_map;
+ PulseConnection *connection;
+ PulseMonitor *monitor;
+ MateMixerAppInfo *app_info;
+};
+
+enum {
+ PROP_0,
+ PROP_INDEX,
+ PROP_CONNECTION,
+ N_PROPERTIES
+};
+
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
+
+static void pulse_stream_control_class_init (PulseStreamControlClass *klass);
+
+static void pulse_stream_control_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void pulse_stream_control_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+
+static void pulse_stream_control_init (PulseStreamControl *control);
+static void pulse_stream_control_dispose (GObject *object);
+static void pulse_stream_control_finalize (GObject *object);
+
+G_DEFINE_ABSTRACT_TYPE (PulseStreamControl, pulse_stream_control, MATE_MIXER_TYPE_STREAM_CONTROL)
+
+static MateMixerAppInfo * pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc);
+
+static gboolean pulse_stream_control_set_mute (MateMixerStreamControl *mmsc,
+ gboolean mute);
+
+static guint pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc);
+
+static guint pulse_stream_control_get_volume (MateMixerStreamControl *mmsc);
+static gboolean pulse_stream_control_set_volume (MateMixerStreamControl *mmsc,
+ guint volume);
+
+static gdouble pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc);
+static gboolean pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc,
+ gdouble decibel);
+
+static guint pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc,
+ guint channel,
+ guint volume);
+
+static gdouble pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel,
+ gdouble decibel);
+
+static MateMixerChannelPosition pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc,
+ guint channel);
+static gboolean pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position);
+
+static gboolean pulse_stream_control_set_balance (MateMixerStreamControl *mmsc,
+ gfloat balance);
+
+static gboolean pulse_stream_control_set_fade (MateMixerStreamControl *mmsc,
+ gfloat fade);
+
+static gboolean pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc);
+static gboolean pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc,
+ gboolean enabled);
+
+static guint pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc);
+static guint pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc);
+static guint pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc);
+static guint pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc);
+
+static void on_monitor_value (PulseMonitor *monitor,
+ gdouble value,
+ PulseStreamControl *control);
+
+static void set_balance_fade (PulseStreamControl *control);
+
+static gboolean set_cvolume (PulseStreamControl *control,
+ pa_cvolume *cvolume);
+
+static void
+pulse_stream_control_class_init (PulseStreamControlClass *klass)
+{
+ GObjectClass *object_class;
+ MateMixerStreamControlClass *control_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = pulse_stream_control_dispose;
+ object_class->finalize = pulse_stream_control_finalize;
+ object_class->get_property = pulse_stream_control_get_property;
+ object_class->set_property = pulse_stream_control_set_property;
+
+ control_class = MATE_MIXER_STREAM_CONTROL_CLASS (klass);
+ control_class->get_app_info = pulse_stream_control_get_app_info;
+ control_class->set_mute = pulse_stream_control_set_mute;
+ control_class->get_num_channels = pulse_stream_control_get_num_channels;
+ control_class->get_volume = pulse_stream_control_get_volume;
+ control_class->set_volume = pulse_stream_control_set_volume;
+ control_class->get_decibel = pulse_stream_control_get_decibel;
+ control_class->set_decibel = pulse_stream_control_set_decibel;
+ control_class->get_channel_volume = pulse_stream_control_get_channel_volume;
+ control_class->set_channel_volume = pulse_stream_control_set_channel_volume;
+ control_class->get_channel_decibel = pulse_stream_control_get_channel_decibel;
+ control_class->set_channel_decibel = pulse_stream_control_set_channel_decibel;
+ control_class->get_channel_position = pulse_stream_control_get_channel_position;
+ control_class->has_channel_position = pulse_stream_control_has_channel_position;
+ control_class->set_balance = pulse_stream_control_set_balance;
+ control_class->set_fade = pulse_stream_control_set_fade;
+ control_class->get_monitor_enabled = pulse_stream_control_get_monitor_enabled;
+ control_class->set_monitor_enabled = pulse_stream_control_set_monitor_enabled;
+ control_class->get_min_volume = pulse_stream_control_get_min_volume;
+ control_class->get_max_volume = pulse_stream_control_get_max_volume;
+ control_class->get_normal_volume = pulse_stream_control_get_normal_volume;
+ control_class->get_base_volume = pulse_stream_control_get_base_volume;
+
+ properties[PROP_INDEX] =
+ g_param_spec_uint ("index",
+ "Index",
+ "Index of the stream control",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CONNECTION] =
+ g_param_spec_object ("connection",
+ "Connection",
+ "PulseAudio connection",
+ PULSE_TYPE_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
+
+ g_type_class_add_private (object_class, sizeof (PulseStreamControlPrivate));
+}
+
+static void
+pulse_stream_control_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ switch (param_id) {
+ case PROP_INDEX:
+ g_value_set_uint (value, control->priv->index);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, control->priv->connection);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_stream_control_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ switch (param_id) {
+ case PROP_INDEX:
+ control->priv->index = g_value_get_uint (value);
+ break;
+ case PROP_CONNECTION:
+ control->priv->connection = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+pulse_stream_control_init (PulseStreamControl *control)
+{
+ control->priv = G_TYPE_INSTANCE_GET_PRIVATE (control,
+ PULSE_TYPE_STREAM_CONTROL,
+ PulseStreamControlPrivate);
+
+ /* Initialize empty volume and channel map structures, they will be used
+ * if the stream does not support volume */
+ pa_cvolume_init (&control->priv->cvolume);
+
+ pa_channel_map_init (&control->priv->channel_map);
+}
+
+static void
+pulse_stream_control_dispose (GObject *object)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ g_clear_object (&control->priv->monitor);
+ g_clear_object (&control->priv->connection);
+
+ G_OBJECT_CLASS (pulse_stream_control_parent_class)->dispose (object);
+}
+
+static void
+pulse_stream_control_finalize (GObject *object)
+{
+ PulseStreamControl *control;
+
+ control = PULSE_STREAM_CONTROL (object);
+
+ if (control->priv->app_info != NULL)
+ _mate_mixer_app_info_free (control->priv->app_info);
+
+ G_OBJECT_CLASS (pulse_stream_control_parent_class)->finalize (object);
+}
+
+guint32
+pulse_stream_control_get_index (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), 0);
+
+ return control->priv->index;
+}
+
+PulseConnection *
+pulse_stream_control_get_connection (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return control->priv->connection;
+}
+
+PulseMonitor *
+pulse_stream_control_get_monitor (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return control->priv->monitor;
+}
+
+const pa_cvolume *
+pulse_stream_control_get_cvolume (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return &control->priv->cvolume;
+}
+
+const pa_channel_map *
+pulse_stream_control_get_channel_map (PulseStreamControl *control)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (control), NULL);
+
+ return &control->priv->channel_map;
+}
+
+void
+pulse_stream_control_set_app_info (PulseStreamControl *control, MateMixerAppInfo *info)
+{
+ g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
+
+ if G_UNLIKELY (control->priv->app_info)
+ _mate_mixer_app_info_free (control->priv->app_info);
+
+ control->priv->app_info = info;
+}
+
+void
+pulse_stream_control_set_channel_map (PulseStreamControl *control, const pa_channel_map *map)
+{
+ MateMixerStreamControlFlags flags;
+
+ g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
+ g_return_if_fail (map != NULL);
+
+ flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control));
+
+ if (pa_channel_map_valid (map)) {
+ if (pa_channel_map_can_balance (map))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_BALANCE;
+
+ if (pa_channel_map_can_fade (map))
+ flags |= MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+ else
+ flags &= ~MATE_MIXER_STREAM_CONTROL_CAN_FADE;
+
+ control->priv->channel_map = *map;
+ } else {
+ flags &= ~(MATE_MIXER_STREAM_CONTROL_CAN_BALANCE | MATE_MIXER_STREAM_CONTROL_CAN_FADE);
+
+ /* If the channel map is not valid, create an empty channel map, which
+ * also won't validate, but at least we know what it is */
+ pa_channel_map_init (&control->priv->channel_map);
+ }
+
+ _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags);
+}
+
+void
+pulse_stream_control_set_cvolume (PulseStreamControl *control,
+ const pa_cvolume *cvolume,
+ pa_volume_t base_volume)
+{
+ MateMixerStreamControlFlags flags;
+
+ g_return_if_fail (PULSE_IS_STREAM_CONTROL (control));
+
+ /* The base volume is not a property */
+ control->priv->base_volume = base_volume;
+
+ flags = mate_mixer_stream_control_get_flags (MATE_MIXER_STREAM_CONTROL (control));
+
+ g_object_freeze_notify (G_OBJECT (control));
+
+ if (cvolume != NULL && pa_cvolume_valid (cvolume)) {
+ /* Decibel volume and volume settability flags must be provided by
+ * the implementation */
+ flags |= MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE;
+
+ if (pa_cvolume_equal (&control->priv->cvolume, cvolume) == 0) {
+ control->priv->cvolume = *cvolume;
+ control->priv->volume = (guint) pa_cvolume_max (&control->priv->cvolume);
+
+ g_object_notify (G_OBJECT (control), "volume");
+ }
+ } else {
+ flags &= ~(MATE_MIXER_STREAM_CONTROL_VOLUME_READABLE |
+ MATE_MIXER_STREAM_CONTROL_VOLUME_WRITABLE |
+ MATE_MIXER_STREAM_CONTROL_HAS_DECIBEL);
+
+ /* If the cvolume is not valid, create an empty cvolume, which also
+ * won't validate, but at least we know what it is */
+ pa_cvolume_init (&control->priv->cvolume);
+
+ if (control->priv->volume != (guint) PA_VOLUME_MUTED) {
+ control->priv->volume = (guint) PA_VOLUME_MUTED;
+
+ g_object_notify (G_OBJECT (control), "volume");
+ }
+ }
+
+ _mate_mixer_stream_control_set_flags (MATE_MIXER_STREAM_CONTROL (control), flags);
+
+ /* Changing volume may change the balance and fade values as well */
+ set_balance_fade (control);
+
+ g_object_thaw_notify (G_OBJECT (control));
+}
+
+static MateMixerAppInfo *
+pulse_stream_control_get_app_info (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), NULL);
+
+ return PULSE_STREAM_CONTROL (mmsc)->priv->app_info;
+}
+
+static gboolean
+pulse_stream_control_set_mute (MateMixerStreamControl *mmsc, gboolean mute)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ return PULSE_STREAM_CONTROL_GET_CLASS (mmsc)->set_mute (PULSE_STREAM_CONTROL (mmsc), mute);
+}
+
+static guint
+pulse_stream_control_get_num_channels (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), 0);
+
+ return PULSE_STREAM_CONTROL (mmsc)->priv->channel_map.channels;
+}
+
+static guint
+pulse_stream_control_get_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return PULSE_STREAM_CONTROL (mmsc)->priv->volume;
+}
+
+static gboolean
+pulse_stream_control_set_volume (MateMixerStreamControl *mmsc, guint volume)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+ cvolume = control->priv->cvolume;
+
+ if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL)
+ return FALSE;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gdouble
+pulse_stream_control_get_decibel (MateMixerStreamControl *mmsc)
+{
+ gdouble value;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
+
+ value = pa_sw_volume_to_dB (pulse_stream_control_get_volume (mmsc));
+
+ /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */
+ return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
+}
+
+static gboolean
+pulse_stream_control_set_decibel (MateMixerStreamControl *mmsc, gdouble decibel)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ return pulse_stream_control_set_volume (mmsc,
+ pa_sw_volume_from_dB (decibel));
+}
+
+static guint
+pulse_stream_control_get_channel_volume (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->cvolume.channels)
+ return (guint) PA_VOLUME_MUTED;
+
+ return (guint) control->priv->cvolume.values[channel];
+}
+
+static gboolean
+pulse_stream_control_set_channel_volume (MateMixerStreamControl *mmsc, guint channel, guint volume)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->cvolume.channels)
+ return FALSE;
+
+ /* This is safe, because the cvolume is validated by set_cvolume() */
+ cvolume = control->priv->cvolume;
+ cvolume.values[channel] = (pa_volume_t) volume;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gdouble
+pulse_stream_control_get_channel_decibel (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseStreamControl *control;
+ gdouble value;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), -MATE_MIXER_INFINITY);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->cvolume.channels)
+ return -MATE_MIXER_INFINITY;
+
+ value = pa_sw_volume_to_dB (control->priv->cvolume.values[channel]);
+
+ return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
+}
+
+static gboolean
+pulse_stream_control_set_channel_decibel (MateMixerStreamControl *mmsc,
+ guint channel,
+ gdouble decibel)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ return pulse_stream_control_set_channel_volume (mmsc,
+ channel,
+ pa_sw_volume_from_dB (decibel));
+}
+
+static MateMixerChannelPosition
+pulse_stream_control_get_channel_position (MateMixerStreamControl *mmsc, guint channel)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), MATE_MIXER_CHANNEL_UNKNOWN);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (channel >= control->priv->channel_map.channels)
+ return MATE_MIXER_CHANNEL_UNKNOWN;
+
+ return pulse_convert_position_to_pulse (control->priv->channel_map.map[channel]);
+}
+
+static gboolean
+pulse_stream_control_has_channel_position (MateMixerStreamControl *mmsc,
+ MateMixerChannelPosition position)
+{
+ PulseStreamControl *control;
+ pa_channel_position_t p;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ /* Handle invalid position as a special case, otherwise this function would
+ * return TRUE for e.g. unknown index in a default channel map */
+ p = pulse_convert_position_to_pulse (position);
+
+ if (p == PA_CHANNEL_POSITION_INVALID)
+ return FALSE;
+
+ if (pa_channel_map_has_position (&control->priv->channel_map, p) != 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+pulse_stream_control_set_balance (MateMixerStreamControl *mmsc, gfloat balance)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+ cvolume = control->priv->cvolume;
+
+ if (pa_cvolume_set_balance (&cvolume, &control->priv->channel_map, balance) == NULL)
+ return FALSE;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gboolean
+pulse_stream_control_set_fade (MateMixerStreamControl *mmsc, gfloat fade)
+{
+ PulseStreamControl *control;
+ pa_cvolume cvolume;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+ cvolume = control->priv->cvolume;
+
+ if (pa_cvolume_set_fade (&cvolume, &control->priv->channel_map, fade) == NULL)
+ return FALSE;
+
+ return set_cvolume (control, &cvolume);
+}
+
+static gboolean
+pulse_stream_control_get_monitor_enabled (MateMixerStreamControl *mmsc)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (control->priv->monitor != NULL)
+ return pulse_monitor_get_enabled (control->priv->monitor);
+
+ return FALSE;
+}
+
+static gboolean
+pulse_stream_control_set_monitor_enabled (MateMixerStreamControl *mmsc, gboolean enabled)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), FALSE);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (enabled == TRUE) {
+ if (control->priv->monitor == NULL) {
+ control->priv->monitor =
+ PULSE_STREAM_CONTROL_GET_CLASS (control)->create_monitor (control);
+
+ if G_UNLIKELY (control->priv->monitor == NULL)
+ return FALSE;
+
+ g_signal_connect (G_OBJECT (control->priv->monitor),
+ "value",
+ G_CALLBACK (on_monitor_value),
+ control);
+ }
+ } else {
+ if (control->priv->monitor == NULL)
+ return FALSE;
+ }
+ return pulse_monitor_set_enabled (control->priv->monitor, enabled);
+}
+
+static guint
+pulse_stream_control_get_min_volume (MateMixerStreamControl *mmsc)
+{
+ return (guint) PA_VOLUME_MUTED;
+}
+
+static guint
+pulse_stream_control_get_max_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return (guint) PA_VOLUME_UI_MAX;
+}
+
+static guint
+pulse_stream_control_get_normal_volume (MateMixerStreamControl *mmsc)
+{
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ return (guint) PA_VOLUME_NORM;
+}
+
+static guint
+pulse_stream_control_get_base_volume (MateMixerStreamControl *mmsc)
+{
+ PulseStreamControl *control;
+
+ g_return_val_if_fail (PULSE_IS_STREAM_CONTROL (mmsc), (guint) PA_VOLUME_MUTED);
+
+ control = PULSE_STREAM_CONTROL (mmsc);
+
+ if (control->priv->base_volume > 0)
+ return control->priv->base_volume;
+ else
+ return (guint) PA_VOLUME_NORM;
+}
+
+static void
+on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStreamControl *control)
+{
+ g_signal_emit_by_name (G_OBJECT (control),
+ "monitor-value",
+ value);
+}
+
+static void
+set_balance_fade (PulseStreamControl *control)
+{
+ gfloat value;
+
+ /* PulseAudio returns the default 0.0f value on error, so skip checking validity
+ * of the channel map and cvolume */
+ value = pa_cvolume_get_balance (&control->priv->cvolume,
+ &control->priv->channel_map);
+
+ _mate_mixer_stream_control_set_balance (MATE_MIXER_STREAM_CONTROL (control), value);
+
+ value = pa_cvolume_get_fade (&control->priv->cvolume,
+ &control->priv->channel_map);
+
+ _mate_mixer_stream_control_set_fade (MATE_MIXER_STREAM_CONTROL (control), value);
+}
+
+static gboolean
+set_cvolume (PulseStreamControl *control, pa_cvolume *cvolume)
+{
+ PulseStreamControlClass *klass;
+
+ if (pa_cvolume_valid (cvolume) == 0)
+ return FALSE;
+ if (pa_cvolume_equal (cvolume, &control->priv->cvolume) != 0)
+ return TRUE;
+
+ klass = PULSE_STREAM_CONTROL_GET_CLASS (control);
+
+ if (klass->set_volume (control, cvolume) == FALSE)
+ return FALSE;
+
+ control->priv->cvolume = *cvolume;
+ control->priv->volume = (guint) pa_cvolume_max (cvolume);
+
+ g_object_notify (G_OBJECT (control), "volume");
+
+ /* Changing volume may change the balance and fade values as well */
+ set_balance_fade (control);
+ return TRUE;
+}
diff --git a/backends/pulse/pulse-stream-control.h b/backends/pulse/pulse-stream-control.h
new file mode 100644
index 0000000..abc3f98
--- /dev/null
+++ b/backends/pulse/pulse-stream-control.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_STREAM_CONTROL_H
+#define PULSE_STREAM_CONTROL_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libmatemixer/matemixer.h>
+
+#include <pulse/pulseaudio.h>
+
+#include "pulse-types.h"
+
+G_BEGIN_DECLS
+
+#define PULSE_TYPE_STREAM_CONTROL \
+ (pulse_stream_control_get_type ())
+#define PULSE_STREAM_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((o), PULSE_TYPE_STREAM_CONTROL, PulseStreamControl))
+#define PULSE_IS_STREAM_CONTROL(o) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((o), PULSE_TYPE_STREAM_CONTROL))
+#define PULSE_STREAM_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_CAST ((k), PULSE_TYPE_STREAM_CONTROL, PulseStreamControlClass))
+#define PULSE_IS_STREAM_CONTROL_CLASS(k) \
+ (G_TYPE_CHECK_CLASS_TYPE ((k), PULSE_TYPE_STREAM_CONTROL))
+#define PULSE_STREAM_CONTROL_GET_CLASS(o) \
+ (G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM_CONTROL, PulseStreamControlClass))
+
+#define PULSE_STREAM_CONTROL_GET_CONNECTION(psc) \
+ (pulse_stream_get_connection (PULSE_STREAM (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc)))))
+#define PULSE_STREAM_CONTROL_GET_STREAM_INDEX(psc) \
+ (pulse_stream_get_index (PULSE_STREAM (mate_mixer_stream_control_get_stream (MATE_MIXER_STREAM_CONTROL (psc)))))
+
+typedef struct _PulseStreamControlClass PulseStreamControlClass;
+typedef struct _PulseStreamControlPrivate PulseStreamControlPrivate;
+
+struct _PulseStreamControl
+{
+ MateMixerStreamControl parent;
+
+ /*< private >*/
+ PulseStreamControlPrivate *priv;
+};
+
+struct _PulseStreamControlClass
+{
+ MateMixerStreamControlClass parent_class;
+
+ /*< private >*/
+ gboolean (*set_mute) (PulseStreamControl *control,
+ gboolean mute);
+ gboolean (*set_volume) (PulseStreamControl *control,
+ pa_cvolume *volume);
+
+ PulseMonitor *(*create_monitor) (PulseStreamControl *control);
+};
+
+GType pulse_stream_control_get_type (void) G_GNUC_CONST;
+
+guint32 pulse_stream_control_get_index (PulseStreamControl *control);
+
+PulseConnection * pulse_stream_control_get_connection (PulseStreamControl *control);
+PulseMonitor * pulse_stream_control_get_monitor (PulseStreamControl *control);
+
+const pa_cvolume * pulse_stream_control_get_cvolume (PulseStreamControl *control);
+const pa_channel_map *pulse_stream_control_get_channel_map (PulseStreamControl *control);
+
+void pulse_stream_control_set_app_info (PulseStreamControl *stream,
+ MateMixerAppInfo *info);
+
+void pulse_stream_control_set_channel_map (PulseStreamControl *control,
+ const pa_channel_map *map);
+void pulse_stream_control_set_cvolume (PulseStreamControl *control,
+ const pa_cvolume *cvolume,
+ pa_volume_t base_volume);
+
+G_END_DECLS
+
+#endif /* PULSE_STREAM_CONTROL_H */
diff --git a/backends/pulse/pulse-stream.c b/backends/pulse/pulse-stream.c
index fb738ad..752c3e6 100644
--- a/backends/pulse/pulse-stream.c
+++ b/backends/pulse/pulse-stream.c
@@ -15,62 +15,34 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <string.h>
#include <glib.h>
#include <glib-object.h>
-
-#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-enums.h>
-#include <libmatemixer/matemixer-stream.h>
-#include <libmatemixer/matemixer-port.h>
+#include <libmatemixer/matemixer.h>
+#include <libmatemixer/matemixer-private.h>
#include <pulse/pulseaudio.h>
#include "pulse-connection.h"
+#include "pulse-device.h"
#include "pulse-helpers.h"
#include "pulse-monitor.h"
+#include "pulse-port.h"
#include "pulse-stream.h"
struct _PulseStreamPrivate
{
- guint32 index;
- gchar *name;
- gchar *description;
- MateMixerDevice *device;
- MateMixerStreamFlags flags;
- MateMixerStreamState state;
- gboolean mute;
- guint volume;
- pa_cvolume cvolume;
- pa_volume_t base_volume;
- pa_channel_map channel_map;
- gfloat balance;
- gfloat fade;
- GHashTable *ports;
- GList *ports_list;
- MateMixerPort *port;
- PulseConnection *connection;
- PulseMonitor *monitor;
- gchar *monitor_name;
+ guint32 index;
+ PulseConnection *connection;
};
enum {
PROP_0,
- PROP_NAME,
- PROP_DESCRIPTION,
- PROP_DEVICE,
- PROP_FLAGS,
- PROP_STATE,
- PROP_MUTE,
- PROP_VOLUME,
- PROP_BALANCE,
- PROP_FADE,
- PROP_ACTIVE_PORT,
PROP_INDEX,
- PROP_CONNECTION
+ PROP_CONNECTION,
+ N_PROPERTIES
};
-static void mate_mixer_stream_interface_init (MateMixerStreamInterface *iface);
+static GParamSpec *properties[N_PROPERTIES] = { NULL, };
static void pulse_stream_class_init (PulseStreamClass *klass);
@@ -83,174 +55,45 @@ static void pulse_stream_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec);
-static void pulse_stream_init (PulseStream *pstream);
+static void pulse_stream_init (PulseStream *stream);
static void pulse_stream_dispose (GObject *object);
-static void pulse_stream_finalize (GObject *object);
-
-G_DEFINE_ABSTRACT_TYPE_WITH_CODE (PulseStream, pulse_stream, G_TYPE_OBJECT,
- G_IMPLEMENT_INTERFACE (MATE_MIXER_TYPE_STREAM,
- mate_mixer_stream_interface_init))
-
-static const gchar * pulse_stream_get_name (MateMixerStream *stream);
-static const gchar * pulse_stream_get_description (MateMixerStream *stream);
-static MateMixerDevice * pulse_stream_get_device (MateMixerStream *stream);
-static MateMixerStreamFlags pulse_stream_get_flags (MateMixerStream *stream);
-static MateMixerStreamState pulse_stream_get_state (MateMixerStream *stream);
-
-static gboolean pulse_stream_get_mute (MateMixerStream *stream);
-static gboolean pulse_stream_set_mute (MateMixerStream *stream,
- gboolean mute);
-
-static guint pulse_stream_get_num_channels (MateMixerStream *stream);
-
-static guint pulse_stream_get_volume (MateMixerStream *stream);
-static gboolean pulse_stream_set_volume (MateMixerStream *stream,
- guint volume);
-
-static gdouble pulse_stream_get_decibel (MateMixerStream *stream);
-static gboolean pulse_stream_set_decibel (MateMixerStream *stream,
- gdouble decibel);
-
-static guint pulse_stream_get_channel_volume (MateMixerStream *stream,
- guint channel);
-static gboolean pulse_stream_set_channel_volume (MateMixerStream *stream,
- guint channel,
- guint volume);
-
-static gdouble pulse_stream_get_channel_decibel (MateMixerStream *stream,
- guint channel);
-static gboolean pulse_stream_set_channel_decibel (MateMixerStream *stream,
- guint channel,
- gdouble decibel);
-
-static MateMixerChannelPosition pulse_stream_get_channel_position (MateMixerStream *stream,
- guint channel);
-static gboolean pulse_stream_has_channel_position (MateMixerStream *stream,
- MateMixerChannelPosition position);
-
-static gfloat pulse_stream_get_balance (MateMixerStream *stream);
-static gboolean pulse_stream_set_balance (MateMixerStream *stream,
- gfloat balance);
-
-static gfloat pulse_stream_get_fade (MateMixerStream *stream);
-static gboolean pulse_stream_set_fade (MateMixerStream *stream,
- gfloat fade);
-
-static gboolean pulse_stream_suspend (MateMixerStream *stream);
-static gboolean pulse_stream_resume (MateMixerStream *stream);
-static gboolean pulse_stream_monitor_start (MateMixerStream *stream);
-static void pulse_stream_monitor_stop (MateMixerStream *stream);
-static gboolean pulse_stream_monitor_is_running (MateMixerStream *stream);
-static gboolean pulse_stream_monitor_set_name (MateMixerStream *stream,
- const gchar *name);
-
-static const GList * pulse_stream_list_ports (MateMixerStream *stream);
-
-static MateMixerPort * pulse_stream_get_active_port (MateMixerStream *stream);
-static gboolean pulse_stream_set_active_port (MateMixerStream *stream,
- MateMixerPort *port);
-
-static guint pulse_stream_get_min_volume (MateMixerStream *stream);
-static guint pulse_stream_get_max_volume (MateMixerStream *stream);
-static guint pulse_stream_get_normal_volume (MateMixerStream *stream);
-static guint pulse_stream_get_base_volume (MateMixerStream *stream);
-
-static void on_monitor_value (PulseMonitor *monitor,
- gdouble value,
- PulseStream *pstream);
-
-static gboolean update_balance_fade (PulseStream *pstream);
-
-static gboolean set_cvolume (PulseStream *pstream,
- pa_cvolume *cvolume);
-
-static gint compare_ports (gconstpointer a,
- gconstpointer b);
-
-static void
-mate_mixer_stream_interface_init (MateMixerStreamInterface *iface)
-{
- iface->get_name = pulse_stream_get_name;
- iface->get_description = pulse_stream_get_description;
- iface->get_device = pulse_stream_get_device;
- iface->get_flags = pulse_stream_get_flags;
- iface->get_state = pulse_stream_get_state;
- iface->get_mute = pulse_stream_get_mute;
- iface->set_mute = pulse_stream_set_mute;
- iface->get_num_channels = pulse_stream_get_num_channels;
- iface->get_volume = pulse_stream_get_volume;
- iface->set_volume = pulse_stream_set_volume;
- iface->get_decibel = pulse_stream_get_decibel;
- iface->set_decibel = pulse_stream_set_decibel;
- iface->get_channel_volume = pulse_stream_get_channel_volume;
- iface->set_channel_volume = pulse_stream_set_channel_volume;
- iface->get_channel_decibel = pulse_stream_get_channel_decibel;
- iface->set_channel_decibel = pulse_stream_set_channel_decibel;
- iface->get_channel_position = pulse_stream_get_channel_position;
- iface->has_channel_position = pulse_stream_has_channel_position;
- iface->get_balance = pulse_stream_get_balance;
- iface->set_balance = pulse_stream_set_balance;
- iface->get_fade = pulse_stream_get_fade;
- iface->set_fade = pulse_stream_set_fade;
- iface->suspend = pulse_stream_suspend;
- iface->resume = pulse_stream_resume;
- iface->monitor_start = pulse_stream_monitor_start;
- iface->monitor_stop = pulse_stream_monitor_stop;
- iface->monitor_is_running = pulse_stream_monitor_is_running;
- iface->monitor_set_name = pulse_stream_monitor_set_name;
- iface->list_ports = pulse_stream_list_ports;
- iface->get_active_port = pulse_stream_get_active_port;
- iface->set_active_port = pulse_stream_set_active_port;
- iface->get_min_volume = pulse_stream_get_min_volume;
- iface->get_max_volume = pulse_stream_get_max_volume;
- iface->get_normal_volume = pulse_stream_get_normal_volume;
- iface->get_base_volume = pulse_stream_get_base_volume;
-}
+G_DEFINE_ABSTRACT_TYPE (PulseStream, pulse_stream, MATE_MIXER_TYPE_STREAM)
static void
pulse_stream_class_init (PulseStreamClass *klass)
{
- GObjectClass *object_class;
+ GObjectClass *object_class;
+ MateMixerStreamClass *stream_class;
object_class = G_OBJECT_CLASS (klass);
object_class->dispose = pulse_stream_dispose;
- object_class->finalize = pulse_stream_finalize;
object_class->get_property = pulse_stream_get_property;
object_class->set_property = pulse_stream_set_property;
- g_object_class_install_property (object_class,
- PROP_INDEX,
- g_param_spec_uint ("index",
- "Index",
- "Stream index",
- 0,
- G_MAXUINT,
- 0,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (object_class,
- PROP_CONNECTION,
- g_param_spec_object ("connection",
- "Connection",
- "PulseAudio connection",
- PULSE_TYPE_CONNECTION,
- G_PARAM_CONSTRUCT_ONLY |
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_override_property (object_class, PROP_NAME, "name");
- g_object_class_override_property (object_class, PROP_DESCRIPTION, "description");
- g_object_class_override_property (object_class, PROP_DEVICE, "device");
- g_object_class_override_property (object_class, PROP_FLAGS, "flags");
- g_object_class_override_property (object_class, PROP_STATE, "state");
- g_object_class_override_property (object_class, PROP_MUTE, "mute");
- g_object_class_override_property (object_class, PROP_VOLUME, "volume");
- g_object_class_override_property (object_class, PROP_BALANCE, "balance");
- g_object_class_override_property (object_class, PROP_FADE, "fade");
- g_object_class_override_property (object_class, PROP_ACTIVE_PORT, "active-port");
+ stream_class = MATE_MIXER_STREAM_CLASS (klass);
+
+ properties[PROP_INDEX] =
+ g_param_spec_uint ("index",
+ "Index",
+ "Index of the stream",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ properties[PROP_CONNECTION] =
+ g_param_spec_object ("connection",
+ "Connection",
+ "PulseAudio connection",
+ PULSE_TYPE_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, N_PROPERTIES, properties);
g_type_class_add_private (object_class, sizeof (PulseStreamPrivate));
}
@@ -261,46 +104,16 @@ pulse_stream_get_property (GObject *object,
GValue *value,
GParamSpec *pspec)
{
- PulseStream *pstream;
+ PulseStream *stream;
- pstream = PULSE_STREAM (object);
+ stream = PULSE_STREAM (object);
switch (param_id) {
- case PROP_NAME:
- g_value_set_string (value, pstream->priv->name);
- break;
- case PROP_DESCRIPTION:
- g_value_set_string (value, pstream->priv->description);
- break;
- case PROP_DEVICE:
- g_value_set_object (value, pstream->priv->device);
- break;
- case PROP_FLAGS:
- g_value_set_flags (value, pstream->priv->flags);
- break;
- case PROP_STATE:
- g_value_set_enum (value, pstream->priv->state);
- break;
- case PROP_MUTE:
- g_value_set_boolean (value, pstream->priv->mute);
- break;
- case PROP_VOLUME:
- g_value_set_uint (value, pstream->priv->volume);
- break;
- case PROP_BALANCE:
- g_value_set_float (value, pstream->priv->balance);
- break;
- case PROP_FADE:
- g_value_set_float (value, pstream->priv->fade);
- break;
- case PROP_ACTIVE_PORT:
- g_value_set_object (value, pstream->priv->port);
- break;
case PROP_INDEX:
- g_value_set_uint (value, pstream->priv->index);
+ g_value_set_uint (value, stream->priv->index);
break;
case PROP_CONNECTION:
- g_value_set_object (value, pstream->priv->connection);
+ g_value_set_object (value, stream->priv->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -314,17 +127,16 @@ pulse_stream_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec)
{
- PulseStream *pstream;
+ PulseStream *stream;
- pstream = PULSE_STREAM (object);
+ stream = PULSE_STREAM (object);
switch (param_id) {
case PROP_INDEX:
- pstream->priv->index = g_value_get_uint (value);
+ stream->priv->index = g_value_get_uint (value);
break;
case PROP_CONNECTION:
- /* Construct-only object */
- pstream->priv->connection = g_value_dup_object (value);
+ stream->priv->connection = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
@@ -333,962 +145,51 @@ pulse_stream_set_property (GObject *object,
}
static void
-pulse_stream_init (PulseStream *pstream)
+pulse_stream_init (PulseStream *stream)
{
- pstream->priv = G_TYPE_INSTANCE_GET_PRIVATE (pstream,
- PULSE_TYPE_STREAM,
- PulseStreamPrivate);
-
- pstream->priv->ports = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
-
- /* Initialize empty volume and channel map structures, they will be used
- * if the stream does not support volume */
- pa_cvolume_init (&pstream->priv->cvolume);
-
- pa_channel_map_init (&pstream->priv->channel_map);
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+ PULSE_TYPE_STREAM,
+ PulseStreamPrivate);
}
static void
pulse_stream_dispose (GObject *object)
{
- PulseStream *pstream;
+ PulseStream *stream;
- pstream = PULSE_STREAM (object);
+ stream = PULSE_STREAM (object);
- if (pstream->priv->ports_list != NULL) {
- g_list_free_full (pstream->priv->ports_list, g_object_unref);
- pstream->priv->ports_list = NULL;
- }
- g_hash_table_remove_all (pstream->priv->ports);
-
- g_clear_object (&pstream->priv->port);
- g_clear_object (&pstream->priv->device);
- g_clear_object (&pstream->priv->monitor);
- g_clear_object (&pstream->priv->connection);
+ g_clear_object (&stream->priv->connection);
G_OBJECT_CLASS (pulse_stream_parent_class)->dispose (object);
}
-static void
-pulse_stream_finalize (GObject *object)
-{
- PulseStream *pstream;
-
- pstream = PULSE_STREAM (object);
-
- g_free (pstream->priv->name);
- g_free (pstream->priv->description);
- g_free (pstream->priv->monitor_name);
-
- g_hash_table_destroy (pstream->priv->ports);
-
- G_OBJECT_CLASS (pulse_stream_parent_class)->finalize (object);
-}
-
guint32
-pulse_stream_get_index (PulseStream *pstream)
+pulse_stream_get_index (PulseStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), 0);
+ g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
- return pstream->priv->index;
+ return stream->priv->index;
}
PulseConnection *
-pulse_stream_get_connection (PulseStream *pstream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
-
- return pstream->priv->connection;
-}
-
-PulseMonitor *
-pulse_stream_get_monitor (PulseStream *pstream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
-
- return pstream->priv->monitor;
-}
-
-const pa_cvolume *
-pulse_stream_get_cvolume (PulseStream *pstream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
-
- return &pstream->priv->cvolume;
-}
-
-const pa_channel_map *
-pulse_stream_get_channel_map (PulseStream *pstream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
-
- return &pstream->priv->channel_map;
-}
-
-GHashTable *
-pulse_stream_get_ports (PulseStream *pstream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), NULL);
-
- return pstream->priv->ports;
-}
-
-gboolean
-pulse_stream_update_name (PulseStream *pstream, const gchar *name)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
-
- /* Allow the name to be NULL */
- if (g_strcmp0 (name, pstream->priv->name) != 0) {
- g_free (pstream->priv->name);
- pstream->priv->name = g_strdup (name);
-
- g_object_notify (G_OBJECT (pstream), "name");
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-pulse_stream_update_description (PulseStream *pstream, const gchar *description)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
-
- /* Allow the description to be NULL */
- if (g_strcmp0 (description, pstream->priv->description) != 0) {
- g_free (pstream->priv->description);
- pstream->priv->description = g_strdup (description);
-
- g_object_notify (G_OBJECT (pstream), "description");
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-pulse_stream_update_device (PulseStream *pstream, MateMixerDevice *device)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
-
- if (pstream->priv->device != device) {
- g_clear_object (&pstream->priv->device);
-
- if (G_LIKELY (device != NULL))
- pstream->priv->device = g_object_ref (device);
-
- g_object_notify (G_OBJECT (pstream), "device");
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-pulse_stream_update_flags (PulseStream *pstream, MateMixerStreamFlags flags)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
-
- if (pstream->priv->flags != flags) {
- pstream->priv->flags = flags;
-
- g_object_notify (G_OBJECT (pstream), "flags");
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-pulse_stream_update_state (PulseStream *pstream, MateMixerStreamState state)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
-
- if (pstream->priv->state != state) {
- pstream->priv->state = state;
-
- g_object_notify (G_OBJECT (pstream), "state");
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-pulse_stream_update_channel_map (PulseStream *pstream, const pa_channel_map *map)
-{
- MateMixerStreamFlags flags;
-
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
- g_return_val_if_fail (map != NULL, FALSE);
-
- flags = pstream->priv->flags;
-
- if (pa_channel_map_valid (map)) {
- if (pa_channel_map_can_balance (map))
- flags |= MATE_MIXER_STREAM_CAN_BALANCE;
- else
- flags &= ~MATE_MIXER_STREAM_CAN_BALANCE;
-
- if (pa_channel_map_can_fade (map))
- flags |= MATE_MIXER_STREAM_CAN_FADE;
- else
- flags &= ~MATE_MIXER_STREAM_CAN_FADE;
-
- pstream->priv->channel_map = *map;
- } else {
- flags &= ~(MATE_MIXER_STREAM_CAN_BALANCE | MATE_MIXER_STREAM_CAN_FADE);
-
- /* If the channel map is not valid, create an empty channel map, which
- * also won't validate, but at least we know what it is */
- pa_channel_map_init (&pstream->priv->channel_map);
- }
-
- pulse_stream_update_flags (pstream, flags);
- return TRUE;
-}
-
-gboolean
-pulse_stream_update_volume (PulseStream *pstream,
- const pa_cvolume *cvolume,
- pa_volume_t base_volume)
-{
- MateMixerStreamFlags flags;
-
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
-
- /* The base volume is not a property */
- pstream->priv->base_volume = base_volume;
-
- flags = pstream->priv->flags;
-
- if (cvolume != NULL && pa_cvolume_valid (cvolume)) {
- /* Decibel volume and volume settability flags must be provided by
- * the implementation */
- flags |= MATE_MIXER_STREAM_HAS_VOLUME;
-
- if (pa_cvolume_equal (&pstream->priv->cvolume, cvolume) == 0) {
- pstream->priv->cvolume = *cvolume;
- pstream->priv->volume = (guint) pa_cvolume_max (&pstream->priv->cvolume);
-
- g_object_notify (G_OBJECT (pstream), "volume");
- }
- } else {
- flags &= ~(MATE_MIXER_STREAM_HAS_VOLUME |
- MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME |
- MATE_MIXER_STREAM_CAN_SET_VOLUME);
-
- /* If the cvolume is not valid, create an empty cvolume, which also
- * won't validate, but at least we know what it is */
- pa_cvolume_init (&pstream->priv->cvolume);
-
- if (pstream->priv->volume != (guint) PA_VOLUME_MUTED) {
- pstream->priv->volume = (guint) PA_VOLUME_MUTED;
-
- g_object_notify (G_OBJECT (pstream), "volume");
- }
- }
-
- pulse_stream_update_flags (pstream, flags);
-
- /* Changing volume may change the balance and fade values as well */
- update_balance_fade (pstream);
- return TRUE;
-}
-
-gboolean
-pulse_stream_update_mute (PulseStream *pstream, gboolean mute)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
-
- if (pstream->priv->mute != mute) {
- pstream->priv->mute = mute;
-
- g_object_notify (G_OBJECT (pstream), "mute");
- return TRUE;
- }
- return FALSE;
-}
-
-gboolean
-pulse_stream_update_active_port (PulseStream *pstream, MateMixerPort *port)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (pstream), FALSE);
- g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
-
- if (pstream->priv->port != port) {
- if (pstream->priv->port != NULL)
- g_clear_object (&pstream->priv->port);
-
- if (port != NULL)
- pstream->priv->port = g_object_ref (port);
-
- g_object_notify (G_OBJECT (pstream), "active-port");
- return TRUE;
- }
- return FALSE;
-}
-
-static const gchar *
-pulse_stream_get_name (MateMixerStream *stream)
+pulse_stream_get_connection (PulseStream *stream)
{
g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
- return PULSE_STREAM (stream)->priv->name;
+ return stream->priv->connection;
}
-static const gchar *
-pulse_stream_get_description (MateMixerStream *stream)
+PulseDevice *
+pulse_stream_get_device (PulseStream *stream)
{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
-
- return PULSE_STREAM (stream)->priv->description;
-}
+ MateMixerDevice *device;
-static MateMixerDevice *
-pulse_stream_get_device (MateMixerStream *stream)
-{
g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
- return PULSE_STREAM (stream)->priv->device;
-}
-
-static MateMixerStreamFlags
-pulse_stream_get_flags (MateMixerStream *stream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_NO_FLAGS);
-
- return PULSE_STREAM (stream)->priv->flags;
-}
-
-static MateMixerStreamState
-pulse_stream_get_state (MateMixerStream *stream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_STREAM_STATE_UNKNOWN);
-
- return PULSE_STREAM (stream)->priv->state;
-}
-
-static gboolean
-pulse_stream_get_mute (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE))
- return FALSE;
-
- return pstream->priv->mute;
-}
-
-static gboolean
-pulse_stream_set_mute (MateMixerStream *stream, gboolean mute)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MUTE))
- return FALSE;
-
- if (pstream->priv->mute != mute) {
- PulseStreamClass *klass = PULSE_STREAM_GET_CLASS (pstream);
-
- if (klass->set_mute (pstream, mute) == FALSE)
- return FALSE;
-
- pstream->priv->mute = mute;
-
- g_object_notify (G_OBJECT (stream), "mute");
- }
- return TRUE;
-}
-
-static guint
-pulse_stream_get_num_channels (MateMixerStream *stream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0);
-
- return PULSE_STREAM (stream)->priv->channel_map.channels;
-}
-
-static guint
-pulse_stream_get_volume (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
- return (guint) PA_VOLUME_MUTED;
-
- return pstream->priv->volume;
-}
-
-static gboolean
-pulse_stream_set_volume (MateMixerStream *stream, guint volume)
-{
- PulseStream *pstream;
- pa_cvolume cvolume;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
- return FALSE;
-
- cvolume = pstream->priv->cvolume;
-
- if (pa_cvolume_scale (&cvolume, (pa_volume_t) volume) == NULL)
- return FALSE;
-
- return set_cvolume (pstream, &cvolume);
-}
-
-static gdouble
-pulse_stream_get_decibel (MateMixerStream *stream)
-{
- PulseStream *pstream;
- gdouble value;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
- return -MATE_MIXER_INFINITY;
-
- value = pa_sw_volume_to_dB (pulse_stream_get_volume (stream));
-
- /* PA_VOLUME_MUTED is converted to PA_DECIBEL_MININFTY */
- return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
-}
-
-static gboolean
-pulse_stream_set_decibel (MateMixerStream *stream, gdouble decibel)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) ||
- !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
- return FALSE;
-
- return pulse_stream_set_volume (stream, pa_sw_volume_from_dB (decibel));
-}
-
-static guint
-pulse_stream_get_channel_volume (MateMixerStream *stream, guint channel)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
- return (guint) PA_VOLUME_MUTED;
-
- if (channel >= pstream->priv->cvolume.channels)
- return (guint) PA_VOLUME_MUTED;
-
- return (guint) pstream->priv->cvolume.values[channel];
-}
-
-static gboolean
-pulse_stream_set_channel_volume (MateMixerStream *stream, guint channel, guint volume)
-{
- PulseStream *pstream;
- pa_cvolume cvolume;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
- return FALSE;
-
- if (channel >= pstream->priv->cvolume.channels)
- return FALSE;
-
- /* This is safe, because the cvolume is validated by set_cvolume() */
- cvolume = pstream->priv->cvolume;
- cvolume.values[channel] = (pa_volume_t) volume;
-
- return set_cvolume (pstream, &cvolume);
-}
-
-static gdouble
-pulse_stream_get_channel_decibel (MateMixerStream *stream, guint channel)
-{
- PulseStream *pstream;
- gdouble value;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), -MATE_MIXER_INFINITY);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME))
- return -MATE_MIXER_INFINITY;
-
- if (channel >= pstream->priv->cvolume.channels)
- return -MATE_MIXER_INFINITY;
-
- value = pa_sw_volume_to_dB (pstream->priv->cvolume.values[channel]);
-
- return (value == PA_DECIBEL_MININFTY) ? -MATE_MIXER_INFINITY : value;
-}
-
-static gboolean
-pulse_stream_set_channel_decibel (MateMixerStream *stream,
- guint channel,
- gdouble decibel)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_DECIBEL_VOLUME) ||
- !(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SET_VOLUME))
- return FALSE;
-
- return pulse_stream_set_channel_volume (stream, channel, pa_sw_volume_from_dB (decibel));
-}
-
-static MateMixerChannelPosition
-pulse_stream_get_channel_position (MateMixerStream *stream, guint channel)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), MATE_MIXER_CHANNEL_UNKNOWN);
-
- pstream = PULSE_STREAM (stream);
-
- if (channel >= pstream->priv->channel_map.channels)
- return MATE_MIXER_CHANNEL_UNKNOWN;
-
- return pulse_convert_position_to_pulse (pstream->priv->channel_map.map[channel]);
-}
-
-static gboolean
-pulse_stream_has_channel_position (MateMixerStream *stream,
- MateMixerChannelPosition position)
-{
- PulseStream *pstream;
- pa_channel_position_t p;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- /* Handle invalid position as a special case, otherwise this function would
- * return TRUE for e.g. unknown index in a default channel map */
- p = pulse_convert_position_to_pulse (position);
-
- if (p == PA_CHANNEL_POSITION_INVALID)
- return FALSE;
-
- if (pa_channel_map_has_position (&pstream->priv->channel_map, p) != 0)
- return TRUE;
- else
- return FALSE;
-}
-
-static gfloat
-pulse_stream_get_balance (MateMixerStream *stream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f);
-
- return PULSE_STREAM (stream)->priv->balance;
-}
-
-static gboolean
-pulse_stream_set_balance (MateMixerStream *stream, gfloat balance)
-{
- PulseStream *pstream;
- pa_cvolume cvolume;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_BALANCE))
- return FALSE;
-
- cvolume = pstream->priv->cvolume;
-
- if (pa_cvolume_set_balance (&cvolume, &pstream->priv->channel_map, balance) == NULL)
- return FALSE;
-
- return set_cvolume (pstream, &cvolume);
-}
-
-static gfloat
-pulse_stream_get_fade (MateMixerStream *stream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), 0.0f);
-
- return PULSE_STREAM (stream)->priv->fade;
-}
-
-static gboolean
-pulse_stream_set_fade (MateMixerStream *stream, gfloat fade)
-{
- PulseStream *pstream;
- pa_cvolume cvolume;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_FADE))
- return FALSE;
-
- cvolume = pstream->priv->cvolume;
-
- if (pa_cvolume_set_fade (&cvolume, &pstream->priv->channel_map, fade) == NULL)
- return FALSE;
-
- return set_cvolume (pstream, &cvolume);
-}
-
-static gboolean
-pulse_stream_suspend (MateMixerStream *stream)
-{
- PulseStream *pstream;
- PulseStreamClass *klass;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
- return FALSE;
-
- if (pstream->priv->state == MATE_MIXER_STREAM_STATE_SUSPENDED)
- return FALSE;
-
- klass = PULSE_STREAM_GET_CLASS (pstream);
-
- if (klass->suspend (pstream) == FALSE)
- return FALSE;
-
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_SUSPENDED);
- return TRUE;
-}
-
-static gboolean
-pulse_stream_resume (MateMixerStream *stream)
-{
- PulseStream *pstream;
- PulseStreamClass *klass;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_CAN_SUSPEND))
- return FALSE;
-
- if (pstream->priv->state != MATE_MIXER_STREAM_STATE_SUSPENDED)
- return FALSE;
-
- klass = PULSE_STREAM_GET_CLASS (pstream);
-
- if (klass->resume (pstream) == FALSE)
- return FALSE;
-
- /* The state when resumed should be either RUNNING or IDLE, let's assume
- * IDLE for now and request an immediate update */
- pulse_stream_update_state (pstream, MATE_MIXER_STREAM_STATE_IDLE);
-
- klass->reload (pstream);
- return TRUE;
-}
-
-static gboolean
-pulse_stream_monitor_start (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_MONITOR))
- return FALSE;
-
- if (pstream->priv->monitor == NULL) {
- pstream->priv->monitor = PULSE_STREAM_GET_CLASS (pstream)->create_monitor (pstream);
-
- if (G_UNLIKELY (pstream->priv->monitor == NULL))
- return FALSE;
-
- pulse_monitor_set_name (pstream->priv->monitor,
- pstream->priv->monitor_name);
-
- g_signal_connect (G_OBJECT (pstream->priv->monitor),
- "value",
- G_CALLBACK (on_monitor_value),
- pstream);
- }
-
- return pulse_monitor_set_enabled (pstream->priv->monitor, TRUE);
-}
-
-static void
-pulse_stream_monitor_stop (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_if_fail (PULSE_IS_STREAM (stream));
-
- pstream = PULSE_STREAM (stream);
-
- if (pstream->priv->monitor != NULL)
- pulse_monitor_set_enabled (pstream->priv->monitor, FALSE);
-}
-
-static gboolean
-pulse_stream_monitor_is_running (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (pstream->priv->monitor != NULL)
- return pulse_monitor_get_enabled (pstream->priv->monitor);
-
- return FALSE;
-}
-
-static gboolean
-pulse_stream_monitor_set_name (MateMixerStream *stream, const gchar *name)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- if (pstream->priv->monitor != NULL)
- pulse_monitor_set_name (pstream->priv->monitor, name);
-
- pstream->priv->monitor_name = g_strdup (name);
- return TRUE;
-}
-
-static const GList *
-pulse_stream_list_ports (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
-
- pstream = PULSE_STREAM (stream);
-
- if (pstream->priv->ports_list == NULL) {
- GList *list = g_hash_table_get_values (pstream->priv->ports);
-
- if (list != NULL) {
- g_list_foreach (list, (GFunc) g_object_ref, NULL);
-
- pstream->priv->ports_list = g_list_sort (list, compare_ports);
- }
- }
-
- return (const GList *) pstream->priv->ports_list;
-}
-
-static MateMixerPort *
-pulse_stream_get_active_port (MateMixerStream *stream)
-{
- g_return_val_if_fail (PULSE_IS_STREAM (stream), NULL);
-
- return PULSE_STREAM (stream)->priv->port;
-}
-
-static gboolean
-pulse_stream_set_active_port (MateMixerStream *stream, MateMixerPort *port)
-{
- PulseStream *pstream;
- PulseStreamClass *klass;
- const gchar *name;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), FALSE);
- g_return_val_if_fail (MATE_MIXER_IS_PORT (port), FALSE);
-
- pstream = PULSE_STREAM (stream);
-
- /* Make sure the port comes from this stream */
- name = mate_mixer_port_get_name (port);
-
- if (g_hash_table_lookup (pstream->priv->ports, name) == NULL) {
- g_warning ("Port %s does not belong to stream %s",
- mate_mixer_port_get_name (port),
- mate_mixer_stream_get_name (stream));
- return FALSE;
- }
-
- klass = PULSE_STREAM_GET_CLASS (pstream);
-
- /* Change the port */
- if (klass->set_active_port (pstream, port) == FALSE)
- return FALSE;
-
- if (pstream->priv->port != NULL)
- g_object_unref (pstream->priv->port);
-
- pstream->priv->port = g_object_ref (port);
-
- g_object_notify (G_OBJECT (stream), "active-port");
- return TRUE;
-}
-
-static guint
-pulse_stream_get_min_volume (MateMixerStream *stream)
-{
- return (guint) PA_VOLUME_MUTED;
-}
-
-static guint
-pulse_stream_get_max_volume (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
- return (guint) PA_VOLUME_MUTED;
-
- return (guint) PA_VOLUME_UI_MAX;
-}
-
-static guint
-pulse_stream_get_normal_volume (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
- return (guint) PA_VOLUME_MUTED;
-
- return (guint) PA_VOLUME_NORM;
-}
-
-static guint
-pulse_stream_get_base_volume (MateMixerStream *stream)
-{
- PulseStream *pstream;
-
- g_return_val_if_fail (PULSE_IS_STREAM (stream), (guint) PA_VOLUME_MUTED);
-
- pstream = PULSE_STREAM (stream);
-
- if (!(pstream->priv->flags & MATE_MIXER_STREAM_HAS_VOLUME))
- return (guint) PA_VOLUME_MUTED;
-
- if (pstream->priv->base_volume > 0)
- return pstream->priv->base_volume;
- else
- return (guint) PA_VOLUME_NORM;
-}
-
-static void
-on_monitor_value (PulseMonitor *monitor, gdouble value, PulseStream *pstream)
-{
- g_signal_emit_by_name (G_OBJECT (pstream),
- "monitor-value",
- value);
-}
-
-static gboolean
-update_balance_fade (PulseStream *pstream)
-{
- gfloat fade;
- gfloat balance;
- gboolean changed = FALSE;
-
- /* The PulseAudio return the default 0.0f values on errors, so skip checking
- * validity of the channel map and volume */
- balance = pa_cvolume_get_balance (&pstream->priv->cvolume,
- &pstream->priv->channel_map);
-
- if (pstream->priv->balance != balance) {
- pstream->priv->balance = balance;
-
- g_object_notify (G_OBJECT (pstream), "balance");
- changed = TRUE;
- }
-
- fade = pa_cvolume_get_fade (&pstream->priv->cvolume,
- &pstream->priv->channel_map);
-
- if (pstream->priv->fade != fade) {
- pstream->priv->fade = fade;
-
- g_object_notify (G_OBJECT (pstream), "fade");
- changed = TRUE;
- }
-
- return changed;
-}
-
-static gboolean
-set_cvolume (PulseStream *pstream, pa_cvolume *cvolume)
-{
- PulseStreamClass *klass;
-
- if (pa_cvolume_valid (cvolume) == 0)
- return FALSE;
- if (pa_cvolume_equal (cvolume, &pstream->priv->cvolume) != 0)
- return TRUE;
-
- klass = PULSE_STREAM_GET_CLASS (pstream);
-
- if (klass->set_volume (pstream, cvolume) == FALSE)
- return FALSE;
-
- pstream->priv->cvolume = *cvolume;
- pstream->priv->volume = (guint) pa_cvolume_max (cvolume);
-
- g_object_notify (G_OBJECT (pstream), "volume");
-
- /* Changing volume may change the balance and fade values as well */
- update_balance_fade (pstream);
- return TRUE;
-}
-
-static gint
-compare_ports (gconstpointer a, gconstpointer b)
-{
- MateMixerPort *p1 = MATE_MIXER_PORT (a);
- MateMixerPort *p2 = MATE_MIXER_PORT (b);
+ device = mate_mixer_stream_get_device (MATE_MIXER_STREAM (stream));
+ if (device != NULL)
+ return PULSE_DEVICE (device);
- gint ret = (gint) (mate_mixer_port_get_priority (p2) -
- mate_mixer_port_get_priority (p1));
- if (ret != 0)
- return ret;
- else
- return strcmp (mate_mixer_port_get_name (p1),
- mate_mixer_port_get_name (p2));
+ return NULL;
}
diff --git a/backends/pulse/pulse-stream.h b/backends/pulse/pulse-stream.h
index e4c6a00..eafe457 100644
--- a/backends/pulse/pulse-stream.h
+++ b/backends/pulse/pulse-stream.h
@@ -20,15 +20,11 @@
#include <glib.h>
#include <glib-object.h>
-
-#include <libmatemixer/matemixer-device.h>
-#include <libmatemixer/matemixer-port.h>
-#include <libmatemixer/matemixer-stream.h>
+#include <libmatemixer/matemixer.h>
#include <pulse/pulseaudio.h>
-#include "pulse-connection.h"
-#include "pulse-monitor.h"
+#include "pulse-types.h"
G_BEGIN_DECLS
@@ -45,13 +41,12 @@ G_BEGIN_DECLS
#define PULSE_STREAM_GET_CLASS(o) \
(G_TYPE_INSTANCE_GET_CLASS ((o), PULSE_TYPE_STREAM, PulseStreamClass))
-typedef struct _PulseStream PulseStream;
typedef struct _PulseStreamClass PulseStreamClass;
typedef struct _PulseStreamPrivate PulseStreamPrivate;
struct _PulseStream
{
- GObject parent;
+ MateMixerStream parent;
/*< private >*/
PulseStreamPrivate *priv;
@@ -59,58 +54,15 @@ struct _PulseStream
struct _PulseStreamClass
{
- GObjectClass parent_class;
-
- /*< private >*/
- /* Virtual table */
- void (*reload) (PulseStream *stream);
-
- gboolean (*set_mute) (PulseStream *stream,
- gboolean mute);
- gboolean (*set_volume) (PulseStream *stream,
- pa_cvolume *volume);
-
- gboolean (*set_active_port) (PulseStream *stream,
- MateMixerPort *port);
-
- gboolean (*suspend) (PulseStream *stream);
- gboolean (*resume) (PulseStream *stream);
-
- PulseMonitor *(*create_monitor) (PulseStream *stream);
+ MateMixerStreamClass parent_class;
};
-GType pulse_stream_get_type (void) G_GNUC_CONST;
-
-guint32 pulse_stream_get_index (PulseStream *pstream);
-PulseConnection * pulse_stream_get_connection (PulseStream *pstream);
-PulseMonitor * pulse_stream_get_monitor (PulseStream *pstream);
-GHashTable * pulse_stream_get_ports (PulseStream *pstream);
-
-const pa_cvolume * pulse_stream_get_cvolume (PulseStream *pstream);
-const pa_channel_map *pulse_stream_get_channel_map (PulseStream *pstream);
-
-gboolean pulse_stream_update_name (PulseStream *pstream,
- const gchar *name);
-gboolean pulse_stream_update_description (PulseStream *pstream,
- const gchar *description);
-gboolean pulse_stream_update_device (PulseStream *pstream,
- MateMixerDevice *device);
-gboolean pulse_stream_update_flags (PulseStream *pstream,
- MateMixerStreamFlags flags);
-gboolean pulse_stream_update_state (PulseStream *pstream,
- MateMixerStreamState state);
-
-gboolean pulse_stream_update_channel_map (PulseStream *pstream,
- const pa_channel_map *map);
-gboolean pulse_stream_update_volume (PulseStream *pstream,
- const pa_cvolume *volume,
- pa_volume_t base_volume);
+GType pulse_stream_get_type (void) G_GNUC_CONST;
-gboolean pulse_stream_update_mute (PulseStream *pstream,
- gboolean mute);
+guint32 pulse_stream_get_index (PulseStream *stream);
+PulseConnection *pulse_stream_get_connection (PulseStream *stream);
-gboolean pulse_stream_update_active_port (PulseStream *pstream,
- MateMixerPort *port);
+PulseDevice * pulse_stream_get_device (PulseStream *stream);
G_END_DECLS
diff --git a/backends/pulse/pulse-types.h b/backends/pulse/pulse-types.h
new file mode 100644
index 0000000..c664268
--- /dev/null
+++ b/backends/pulse/pulse-types.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 Michal Ratajsky <[email protected]>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PULSE_TYPES_H
+#define PULSE_TYPES_H
+
+G_BEGIN_DECLS
+
+typedef struct _PulseBackend PulseBackend;
+typedef struct _PulseConnection PulseConnection;
+typedef struct _PulseDevice PulseDevice;
+typedef struct _PulseDeviceProfile PulseDeviceProfile;
+typedef struct _PulseDeviceSwitch PulseDeviceSwitch;
+typedef struct _PulseExtStream PulseExtStream;
+typedef struct _PulseMonitor PulseMonitor;
+typedef struct _PulsePort PulsePort;
+typedef struct _PulsePortSwitch PulsePortSwitch;
+typedef struct _PulseSink PulseSink;
+typedef struct _PulseSinkControl PulseSinkControl;
+typedef struct _PulseSinkInput PulseSinkInput;
+typedef struct _PulseSinkSwitch PulseSinkSwitch;
+typedef struct _PulseSource PulseSource;
+typedef struct _PulseSourceControl PulseSourceControl;
+typedef struct _PulseSourceOutput PulseSourceOutput;
+typedef struct _PulseSourceSwitch PulseSourceSwitch;
+typedef struct _PulseStream PulseStream;
+typedef struct _PulseStreamControl PulseStreamControl;
+
+G_END_DECLS
+
+#endif /* PULSE_TYPES_H */