summaryrefslogtreecommitdiff
path: root/backends/oss
diff options
context:
space:
mode:
Diffstat (limited to 'backends/oss')
-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
14 files changed, 1534 insertions, 478 deletions
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 */