summaryrefslogtreecommitdiff
path: root/cmd/ossxmix
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/ossxmix')
-rw-r--r--cmd/ossxmix/.config4
-rw-r--r--cmd/ossxmix/gtkvu.c304
-rw-r--r--cmd/ossxmix/gtkvu.h61
-rw-r--r--cmd/ossxmix/ossxmix.c2337
-rw-r--r--cmd/ossxmix/ossxmix.man42
-rw-r--r--cmd/ossxmix/ossxmix.xpm312
6 files changed, 3060 insertions, 0 deletions
diff --git a/cmd/ossxmix/.config b/cmd/ossxmix/.config
new file mode 100644
index 0000000..67bc926
--- /dev/null
+++ b/cmd/ossxmix/.config
@@ -0,0 +1,4 @@
+cflags=$GTKCFLAGS
+ldflags=$GTKLDFLAGS
+depends=GTK
+forgetos=VxWorks
diff --git a/cmd/ossxmix/gtkvu.c b/cmd/ossxmix/gtkvu.c
new file mode 100644
index 0000000..e5f6e36
--- /dev/null
+++ b/cmd/ossxmix/gtkvu.c
@@ -0,0 +1,304 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#ifdef __hpux
+#define G_INLINE_FUNC
+#endif
+
+#include <stdio.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtksignal.h>
+
+#include "gtkvu.h"
+
+extern int width_adjust;
+
+#define SCROLL_DELAY_LENGTH 300
+#define VU_DEFAULT_SIZE 100
+#define VU_MARGIN (widget->allocation.width/3)
+
+/* Forward declarations */
+
+static void gtk_vu_class_init (GtkVUClass * klass);
+static void gtk_vu_init (GtkVU * vu);
+static void gtk_vu_destroy (GtkObject * object);
+static void gtk_vu_realize (GtkWidget * widget);
+static void gtk_vu_unrealize (GtkWidget * widget);
+static void gtk_vu_size_request (GtkWidget * widget,
+ GtkRequisition * requisition);
+static void gtk_vu_size_allocate (GtkWidget * widget,
+ GtkAllocation * allocation);
+static gint gtk_vu_expose (GtkWidget * widget, GdkEventExpose * event);
+
+/* Local data */
+
+static GtkWidgetClass *parent_class = NULL;
+
+GtkType
+gtk_vu_get_type (void)
+{
+ static GtkType vu_type = 0;
+
+ if (!vu_type)
+ {
+ GtkTypeInfo vu_info = {
+ "GtkVU",
+ sizeof (GtkVU),
+ sizeof (GtkVUClass),
+ (GtkClassInitFunc) gtk_vu_class_init,
+ (GtkObjectInitFunc) gtk_vu_init,
+ NULL,
+ NULL,
+ };
+
+ vu_type = gtk_type_unique (gtk_widget_get_type (), &vu_info);
+ }
+
+ return vu_type;
+}
+
+static void
+gtk_vu_class_init (GtkVUClass * gvclass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = (GtkObjectClass *) gvclass;
+ widget_class = (GtkWidgetClass *) gvclass;
+
+ parent_class = GTK_WIDGET_CLASS (gtk_type_class (gtk_widget_get_type ()));
+
+ object_class->destroy = gtk_vu_destroy;
+
+ widget_class->realize = gtk_vu_realize;
+ widget_class->unrealize = gtk_vu_unrealize;
+ widget_class->expose_event = gtk_vu_expose;
+ widget_class->size_request = gtk_vu_size_request;
+ widget_class->size_allocate = gtk_vu_size_allocate;
+}
+
+static void
+gtk_vu_init (GtkVU * vu)
+{
+ vu->level = 0;
+}
+
+GtkWidget *
+gtk_vu_new (void)
+{
+ GtkVU *vu;
+
+ vu = GTK_VU (gtk_type_new (gtk_vu_get_type ()));
+
+ return GTK_WIDGET (vu);
+}
+
+static void
+gtk_vu_destroy (GtkObject * object)
+{
+ /* GtkVU *vu; */
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GTK_IS_VU (object));
+
+ /* vu = GTK_VU (object); */
+
+ if (GTK_OBJECT_CLASS (parent_class)->destroy)
+ (*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gtk_vu_realize (GtkWidget * widget)
+{
+ GtkVU *vu;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+ gboolean alloc_success[7];
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_VU (widget));
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+ vu = GTK_VU (widget);
+
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+ widget->window =
+ gdk_window_new (widget->parent->window, &attributes, attributes_mask);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_user_data (widget->window, widget);
+
+ /* Dark green */
+
+ vu->colors[0].red = 0x0000;
+ vu->colors[0].green = 0x30FF;
+ vu->colors[0].blue = 0x0000;
+
+ /* Green */
+
+ vu->colors[1].red = 0x0000;
+ vu->colors[1].green = 0xBFFF;
+ vu->colors[1].blue = 0x0000;
+
+ /* Dark Orange */
+
+ vu->colors[2].red = 0x30FF;
+ vu->colors[2].green = 0x30FF;
+ vu->colors[2].blue = 0x0000;
+
+ /* Orange */
+
+ vu->colors[3].red = 0xBFFF;
+ vu->colors[3].green = 0xBFFF;
+ vu->colors[3].blue = 0x0000;
+
+ /* Dark Red */
+
+ vu->colors[4].red = 0x30FF;
+ vu->colors[4].green = 0x0000;
+ vu->colors[4].blue = 0x0000;
+
+ /* Red */
+
+ vu->colors[5].red = 0xBFFF;
+ vu->colors[5].green = 0x0000;
+ vu->colors[5].blue = 0x0000;
+
+ /* Black */
+
+ vu->colors[6].red = 0x0000;
+ vu->colors[6].green = 0x0000;
+ vu->colors[6].blue = 0x0000;
+
+ gdk_colormap_alloc_colors (gtk_widget_get_colormap (widget), vu->colors, 7,
+ FALSE, TRUE, alloc_success);
+ vu->gc = gdk_gc_new (widget->window);
+ vu->pixmap =
+ gdk_pixmap_new (widget->window, widget->allocation.width,
+ widget->allocation.height, -1);
+
+ /* gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE); */
+}
+
+static void
+gtk_vu_unrealize (GtkWidget * widget)
+{
+ GtkVU *vu;
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_VU (widget));
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED);
+ vu = GTK_VU (widget);
+
+ gdk_colormap_free_colors (gtk_widget_get_colormap (widget), vu->colors, 7);
+ gdk_pixmap_unref (vu->pixmap);
+ gdk_gc_unref (vu->gc);
+ gdk_window_unref (widget->window);
+}
+
+/*ARGSUSED*/
+static void
+gtk_vu_size_request (GtkWidget * widget, GtkRequisition * requisition)
+{
+ if (width_adjust <= 0)
+ requisition->width = 20;
+ else
+ requisition->width = 28;
+ requisition->height = 85;
+}
+
+static void
+gtk_vu_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
+{
+ GtkVU *vu;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_VU (widget));
+ g_return_if_fail (allocation != NULL);
+
+ widget->allocation = *allocation;
+ vu = GTK_VU (widget);
+
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+ gdk_pixmap_unref (vu->pixmap);
+ vu->pixmap =
+ gdk_pixmap_new (widget->window, widget->allocation.width,
+ widget->allocation.height,
+ gdk_window_get_visual (widget->window)->depth);
+ }
+}
+
+static gint
+gtk_vu_expose (GtkWidget * widget, GdkEventExpose * event)
+{
+ GtkVU *vu;
+ guint i, y_size;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_VU (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->count > 0)
+ return FALSE;
+
+ vu = GTK_VU (widget);
+
+ gdk_gc_set_foreground (vu->gc, &vu->colors[6]);
+ gdk_draw_rectangle (vu->pixmap, vu->gc, TRUE, 0, 0,
+ widget->allocation.width, widget->allocation.height);
+ /* gdk_window_clear_area (vu->pixmap,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height); */
+
+ y_size = (widget->allocation.height - 50) / 8;
+
+ for (i = 0; i < 8; i++)
+ {
+ gdk_gc_set_foreground (vu->gc,
+ &vu->colors[((7 - i) / 3) * 2 +
+ (((8 - vu->level) > i) ? 0 : 1)]);
+ gdk_draw_rectangle (vu->pixmap, vu->gc, TRUE, VU_MARGIN,
+ (i * (y_size + 5)) + 10,
+ widget->allocation.width - VU_MARGIN * 2, y_size);
+ }
+
+ gdk_draw_pixmap (widget->window, vu->gc, vu->pixmap, 0, 0, 0, 0,
+ widget->allocation.width, widget->allocation.height);
+ return FALSE;
+}
+
+void
+gtk_vu_set_level (GtkVU * vu, guint new_level)
+{
+ if (new_level != vu->level)
+ {
+ vu->level = new_level;
+ gtk_widget_queue_draw (GTK_WIDGET (vu));
+ }
+}
diff --git a/cmd/ossxmix/gtkvu.h b/cmd/ossxmix/gtkvu.h
new file mode 100644
index 0000000..344d566
--- /dev/null
+++ b/cmd/ossxmix/gtkvu.h
@@ -0,0 +1,61 @@
+#ifndef __GTK_VU_H__
+#define __GTK_VU_H__
+
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include <gdk/gdk.h>
+#include <gtk/gtkadjustment.h>
+#include <gtk/gtkwidget.h>
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+
+#define GTK_VU(obj) GTK_CHECK_CAST (obj, gtk_vu_get_type (), GtkVU)
+#define GTK_VU_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, gtk_vu_get_type (), GtkVUClass)
+#define GTK_IS_VU(obj) GTK_CHECK_TYPE (obj, gtk_vu_get_type ())
+
+
+ typedef struct _GtkVU GtkVU;
+ typedef struct _GtkVUClass GtkVUClass;
+
+ struct _GtkVU
+ {
+ GtkWidget widget;
+
+ guint level;
+ GdkGC *gc;
+ GdkPixmap *pixmap;
+ GdkColor colors[7];
+
+ };
+
+ struct _GtkVUClass
+ {
+ GtkWidgetClass parent_class;
+ };
+
+
+ GtkWidget *gtk_vu_new (void);
+ GtkType gtk_vu_get_type (void);
+ void gtk_vu_set_level (GtkVU * vu, guint new_level);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_VU_H__ */
diff --git a/cmd/ossxmix/ossxmix.c b/cmd/ossxmix/ossxmix.c
new file mode 100644
index 0000000..5f92174
--- /dev/null
+++ b/cmd/ossxmix/ossxmix.c
@@ -0,0 +1,2337 @@
+/*
+ * Purpose: This is the ossxmix (GTK++ GUI) program shipped with OSS
+ *
+ * Description:
+ * The {!xlink ossxmix} program is the primary mixer and control panel utility
+ * available for OSS. It shows how the new mixer API of OSS can be
+ * used in GUI type of programs See the "{!link mixer}" section of the
+ * OSS Developer's manual for more info.
+ *
+ * This program is fully dynamic as required by the mixer interface. It doesn't
+ * contain anything that is specific to certain device. All the mixer structure
+ * information is loaded in the beginning of the program by using the
+ * {!nlink SNDCTL_MIX_EXTINFO} ioctl (and the related calls).
+ *
+ * Note that this program was written before the final mixer API
+ * was ready. For this reason handling of some special situations is missing
+ * or incompletely implemented. For example handling of the
+ * {!nlink EIDRM} is "emulated" simply by closing and re-execing the
+ * program. This is bit iritating but works.
+ *
+ * What might be interesting in this program is how to create the GUI layout
+ * based on the control tree obtained using the SNDCTL_MIX_EXTINFO routine.
+ * However unfortunately this part of the program is not particularily easy
+ * understand.
+ *
+ * {!notice Please read the mixer programming documentation very carefully
+ * before studying this program.
+ *
+ * The {!nlink ossmix.c} program is a command line version of this one.
+ *
+ * The {!nlink mixext.c} program is a very simple program that shows how
+ * "non-mixer" applications can do certain mixer changes.
+ *
+ * This program uses a "LED" bar widget contained in gtkvu.c.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#ifdef __hpux
+#define G_INLINE_FUNC
+#endif
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <soundcard.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include "gtkvu.h"
+#include "ossxmix.xpm"
+
+#undef TEST_JOY
+#undef DEBUG
+
+#ifndef GTK1_ONLY
+#include <gtk/gtkversion.h>
+#if GTK_CHECK_VERSION(2,10,0) && !defined(GDK_WINDOWING_DIRECTFB)
+#define STATUSICON
+#endif /* GTK_CHECK_VERSION(2,10,0) && !GDK_WINDOWING_DIRECTFB */
+#else
+#include <gdk/gdkx.h>
+#endif /* !GTK1_ONLY */
+
+#ifdef TEST_JOY
+#include "gtkjoy.h"
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+static int boomer_workaround = 0;
+
+#define MAX_DEVS 16
+static int set_counter[MAX_DEVS] = { 0 };
+static int prev_update_counter[MAX_DEVS] = { 0 };
+static oss_mixext_root * root[MAX_DEVS] = { NULL };
+static oss_mixext * extrec[MAX_DEVS] = { NULL };
+static int global_fd = -1; /* Global /dev/mixer fd for SNDCTL_SYSINFO/MIXERINFO/etc */
+static int local_fd[MAX_DEVS]; /* Mixer specific fd(s) for actual mixer access */
+static int dev = -1;
+static int show_all = 1;
+static int fully_started = 0;
+static int load_all_devs = 1;
+static int background = 0;
+static int show_status_icon = 1;
+
+int width_adjust = 0;
+
+#define LEFT 1
+#define RIGHT 2
+#define MONO 3
+#define BOTH 4
+
+static guint poll_tag_list[4] = { 0 };
+#define PEAK_DECAY 6
+#define PEAK_POLL_INTERVAL 50
+#define VALUE_POLL_INTERVAL 5000
+#define MIXER_POLL_INTERVAL 100
+#define MIXNUM_POLL_INTERVAL 5000
+static int mixer_num;
+
+#ifndef EIDRM
+#define EIDRM EFAULT
+#endif
+
+typedef enum uflag {
+ WHAT_LABEL,
+ WHAT_UPDATE,
+ WHAT_VMIX
+}
+uflag_t;
+
+typedef struct ctlrec
+{
+ struct ctlrec *next;
+ oss_mixext *mixext;
+ GtkObject *left, *right;
+ GtkWidget *gang, *frame;
+#define FRAME_NAME_LENGTH 8
+ char frame_name[FRAME_NAME_LENGTH+1];
+ int last_left, last_right;
+ int full_scale;
+ uflag_t what_to_do;
+ int parm;
+}
+ctlrec_t;
+
+static ctlrec_t * control_list[MAX_DEVS] = { NULL };
+static ctlrec_t * peak_list[MAX_DEVS] = { NULL };
+static ctlrec_t * value_poll_list[MAX_DEVS] = { NULL };
+static ctlrec_t * check_list[MAX_DEVS] = { NULL };
+
+static GtkWidget * window, * scrolledwin;
+
+static gint add_timeout (gpointer);
+static void change_enum (GtkToggleButton *, gpointer);
+static void change_on_off (GtkToggleButton *, gpointer);
+static void check_tooltip (oss_mixext *, GtkWidget *);
+static void cleanup (void);
+static gint close_request (GtkWidget *, gpointer);
+static void connect_enum (oss_mixext *, GtkObject *);
+static void connect_onoff (oss_mixext *, GtkObject *);
+static void connect_peak (oss_mixext *, GtkWidget *, GtkWidget *);
+static void connect_scrollers (oss_mixext *, GtkObject *,
+ GtkObject *, GtkWidget *);
+static void connect_value_poll (oss_mixext *, GtkWidget *);
+static void create_update (GtkWidget *, GtkObject *, GtkObject *, GtkWidget *,
+ oss_mixext *, uflag_t, int);
+static GtkRequisition create_widgets (void);
+static char * cut_name (char *);
+static void do_update (ctlrec_t *);
+static int findenum (oss_mixext *, const char *);
+static int find_default_mixer (void);
+static void gang_change (GtkToggleButton *, gpointer);
+static int get_fd (int);
+static int get_value (oss_mixext *);
+static GtkWidget * load_devinfo (int);
+static GList * load_enum_values (char *, oss_mixext *);
+static GtkWidget * load_multiple_devs (void);
+static void manage_label (GtkWidget *, oss_mixext *);
+#ifndef GTK1_ONLY
+static gint manage_timeouts (GtkWidget *, GdkEventWindowState *, gpointer);
+#endif /* !GTK1_ONLY */
+static void parse_dimarg (const char *, GtkRequisition *);
+static gint poll_all (gpointer);
+static gint poll_peaks (gpointer);
+static gint poll_values (gpointer);
+static gint poll_mixnum (gpointer);
+static void reload_gui (void);
+static gint remove_timeout (gpointer);
+static void Scrolled (GtkAdjustment *, gpointer);
+static int set_value (oss_mixext *, int);
+static char * showenum (oss_mixext *, int);
+static void store_name (int, int, char *, char **);
+static void switch_page (GtkNotebook *, GtkNotebookPage *, guint, gpointer);
+static void update_label (oss_mixext *, GtkWidget *, int);
+#ifdef STATUSICON
+static void activate_mainwindow (GtkStatusIcon *, guint, guint, gpointer);
+static void popup_mainwindow (GtkWidget *, gpointer);
+static void trayicon_popupmenu (GtkStatusIcon *, guint, guint, gpointer);
+
+static GtkStatusIcon *status_icon = NULL;
+#endif /* STATUSICON */
+
+static int
+get_fd (int dev)
+{
+ int fd;
+ oss_mixerinfo mi;
+
+ if (dev < 0 || dev >= MAX_DEVS)
+ {
+ fprintf (stderr, "Bad mixer device number %d\n", dev);
+ exit (EXIT_FAILURE);
+ }
+
+ if (local_fd[dev] != -1)
+ return local_fd[dev];
+
+ mi.dev = dev;
+ if (ioctl (global_fd, SNDCTL_MIXERINFO, &mi) == -1)
+ {
+ perror ("SNDCTL_MIXERINFO");
+ exit (EXIT_FAILURE);
+ }
+
+ if ((fd = open (mi.devnode, O_RDWR, 0)) == -1)
+ {
+ perror (mi.devnode);
+ }
+
+ return local_fd[dev] = fd;
+}
+
+static void
+check_tooltip (oss_mixext * rec, GtkWidget * wid)
+{
+ oss_mixer_enuminfo ei;
+ char *p;
+
+ if (!(rec->flags & MIXF_DESCR)) /* No description available */
+ return;
+
+ ei.dev = rec->dev;
+ ei.ctrl = rec->ctrl;
+
+ if (ioctl (get_fd(rec->dev), SNDCTL_MIX_DESCRIPTION, &ei) == -1)
+ return;
+
+/*
+ * Separate the first line which contains the tooltip from the subsequent lines
+ * which contain the optional help text.
+ */
+ p=ei.strings;
+
+ while (*p && *p != '\n') p++; /* Find a line feed */
+
+ if (*p=='\n')
+ *p++=0;
+ if (*p==0)
+ p=NULL;
+
+#if GTK_CHECK_VERSION(2,12,0)
+ gtk_widget_set_tooltip_text(wid, ei.strings);
+#else
+ {
+ GtkTooltips *tip;
+
+ tip = gtk_tooltips_new();
+ gtk_tooltips_set_tip(tip, wid, ei.strings, p);
+ }
+#endif
+}
+
+static void
+store_name (int dev, int n, char *name, char **extnames)
+{
+ char *src = name;
+ size_t i, l;
+
+ l = strlen (name);
+ for (i = 0; i < l; i++)
+ {
+ if (name[i] >= 'A' && name[i] <= 'Z')
+ name[i] += 32;
+ if (name[i] == '.') src = name + i + 1;
+ }
+
+ extnames[n] = g_strdup (src);
+#ifdef DEBUG
+ fprintf (stderr, "Control = %s\n", name);
+#endif
+}
+
+static char *
+cut_name (char * name)
+{
+ char *s = name;
+ while (*s)
+ if (*s++ == '_')
+ return s;
+
+ if (name[0] == '@')
+ return &name[1];
+
+ return name;
+}
+
+static char *
+showenum (oss_mixext * rec, int val)
+{
+ static char tmp[100];
+ oss_mixer_enuminfo ei;
+
+ if (val > rec->maxvalue)
+ {
+ snprintf (tmp, sizeof(tmp), "%d(too large (%d)?)", val, rec->maxvalue);
+ return tmp;
+ }
+
+ ei.dev = rec->dev;
+ ei.ctrl = rec->ctrl;
+
+ if (ioctl (get_fd(rec->dev), SNDCTL_MIX_ENUMINFO, &ei) != -1)
+ {
+ char *p;
+
+ if (val >= ei.nvalues)
+ {
+ snprintf (tmp, sizeof(tmp), "%d(too large2 (%d)?)", val, ei.nvalues);
+ return tmp;
+ }
+
+ p = ei.strings + ei.strindex[val];
+ strncpy (tmp, p, sizeof(tmp));
+ return tmp;
+ }
+
+ snprintf (tmp, sizeof(tmp), "%d", val);
+ return tmp;
+}
+
+static GList *
+load_enum_values (char *extname, oss_mixext * rec)
+{
+ int i;
+ GList *list = NULL;
+ oss_mixer_enuminfo ei;
+
+ ei.dev = rec->dev;
+ ei.ctrl = rec->ctrl;
+
+ if (ioctl (get_fd(rec->dev), SNDCTL_MIX_ENUMINFO, &ei) != -1)
+ {
+ int n = ei.nvalues;
+ char *p;
+
+ if (n > rec->maxvalue)
+ n = rec->maxvalue;
+
+ for (i = 0; i < rec->maxvalue; i++)
+ if (rec->enum_present[i / 8] & (1 << (i % 8)))
+ {
+ p = ei.strings + ei.strindex[i];
+ list = g_list_append (list, g_strdup (p));
+ }
+
+ return list;
+ }
+
+ if (*extname == '.')
+ extname++;
+
+ for (i = 0; i < rec->maxvalue; i++)
+ if (rec->enum_present[i / 8] & (1 << (i % 8)))
+ {
+ list = g_list_append (list, g_strdup (showenum (rec, i)));
+ }
+
+ return list;
+}
+
+static int
+findenum (oss_mixext * rec, const char * arg)
+{
+ int i, v;
+ oss_mixer_enuminfo ei;
+
+ ei.dev = rec->dev;
+ ei.ctrl = rec->ctrl;
+
+ if (ioctl (get_fd(rec->dev), SNDCTL_MIX_ENUMINFO, &ei) != -1)
+ {
+ int n = ei.nvalues;
+ char *p;
+
+ if (n > rec->maxvalue)
+ n = rec->maxvalue;
+
+ for (i = 0; i < rec->maxvalue; i++)
+ if (rec->enum_present[i / 8] & (1 << (i % 8)))
+ {
+ p = ei.strings + ei.strindex[i];
+ if (strcmp (p, arg) == 0)
+ return i;
+ }
+ }
+
+ if (sscanf (arg, "%d", &v) == 1)
+ return v;
+
+ fprintf (stderr, "Invalid enumerated value '%s'\n", arg);
+ return 0;
+}
+
+static int
+get_value (oss_mixext * thisrec)
+{
+ oss_mixer_value val;
+
+ val.dev = thisrec->dev;
+ val.ctrl = thisrec->ctrl;
+ val.timestamp = thisrec->timestamp;
+
+ if (ioctl (get_fd(thisrec->dev), SNDCTL_MIX_READ, &val) == -1)
+ {
+ if (errno == EPIPE)
+ {
+#if 0
+ fprintf (stderr,
+ "ossxmix: Mixer device disconnected from the system\n");
+#endif
+ return 0;
+ }
+
+ if (errno == EIDRM)
+ {
+ if (fully_started)
+ {
+/*
+ * The mixer structure got changed for some reason. This program
+ * is not designed to handle this event properly so all we can do
+ * is to recreate the entire GUI.
+ *
+ * Well written applications should just dispose the changed GUI elements
+ * (by comparing the {!code timestamp} fields. Then the new fields can be
+ * created just like we did when starting the program.
+ */
+ fprintf (stderr,
+ "ossxmix: Mixer structure changed - restarting.\n");
+ reload_gui ();
+ return -1;
+ }
+ else
+ {
+ fprintf (stderr,
+ "ossxmix: Mixer structure changed - aborting.\n");
+ exit (-1);
+ }
+ }
+ fprintf (stderr, "%s\n", thisrec->id);
+ perror ("SNDCTL_MIX_READ");
+ return -1;
+ }
+
+ return val.value;
+}
+
+static int
+set_value (oss_mixext * thisrec, int value)
+{
+ oss_mixer_value val;
+
+ if (!(thisrec->flags & MIXF_WRITEABLE))
+ return -1;
+ val.dev = thisrec->dev;
+ val.ctrl = thisrec->ctrl;
+ val.value = value;
+ val.timestamp = thisrec->timestamp;
+ set_counter[thisrec->dev]++;
+
+ if (ioctl (get_fd(thisrec->dev), SNDCTL_MIX_WRITE, &val) == -1)
+ {
+ if (errno == EIDRM)
+ {
+ if (fully_started)
+ {
+ fprintf (stderr,
+ "ossxmix: Mixer structure changed - restarting.\n");
+ reload_gui ();
+ return -1;
+ }
+ else
+ {
+ fprintf (stderr,
+ "ossxmix: Mixer structure changed - aborting.\n");
+ exit (-1);
+ }
+ }
+ fprintf (stderr, "%s\n", thisrec->id);
+ perror ("SNDCTL_MIX_WRITE");
+ return -1;
+ }
+ return val.value;
+}
+
+static void
+create_update (GtkWidget * frame, GtkObject * left, GtkObject * right,
+ GtkWidget * gang, oss_mixext * thisrec, uflag_t what, int parm)
+{
+ ctlrec_t *srec;
+
+ srec = g_new (ctlrec_t, 1);
+ srec->mixext = thisrec;
+ srec->parm = parm;
+ srec->what_to_do = what;
+ srec->frame = frame;
+ srec->left = left;
+ srec->right = right;
+ srec->gang = gang;
+ srec->frame_name[0] = '\0';
+
+ srec->next = check_list[thisrec->dev];
+ check_list[thisrec->dev] = srec;
+}
+
+static void
+manage_label (GtkWidget * frame, oss_mixext * thisrec)
+{
+ char new_label[FRAME_NAME_LENGTH+1], tmp[16];
+
+ if (thisrec->id[0] != '@')
+ return;
+
+ *new_label = 0;
+
+ strncpy (tmp, &thisrec->id[1], sizeof(tmp));
+ tmp[15] = '\0';
+
+ if ((tmp[0] == 'd' && tmp[1] == 's' && tmp[2] == 'p') ||
+ (tmp[0] == 'p' && tmp[1] == 'c' && tmp[2] == 'm'))
+ {
+ int dspnum;
+ oss_audioinfo ainfo;
+
+ if (sscanf (&tmp[3], "%d", &dspnum) != 1)
+ return;
+
+ ainfo.dev = dspnum;
+ if (ioctl (global_fd, SNDCTL_ENGINEINFO, &ainfo) == -1)
+ {
+ perror ("SNDCTL_ENGINEINFO");
+ return;
+ }
+ create_update (frame, NULL, NULL, NULL, thisrec, WHAT_LABEL, dspnum);
+ if (*ainfo.label != 0)
+ {
+ strncpy (new_label, ainfo.label, FRAME_NAME_LENGTH);
+ new_label[FRAME_NAME_LENGTH] = 0;
+ }
+ }
+
+
+ if (*new_label != 0)
+ gtk_frame_set_label (GTK_FRAME (frame), new_label);
+
+}
+
+static void
+Scrolled (GtkAdjustment * adjust, gpointer data)
+{
+ int val, origval, lval, rval;
+ int side, gang_on;
+ ctlrec_t *srec = (ctlrec_t *) data;
+ int shift = 8;
+
+ val = srec->mixext->maxvalue - (int) adjust->value;
+ origval = (int) adjust->value;
+
+ if (srec->mixext->type == MIXT_MONOSLIDER16
+ || srec->mixext->type == MIXT_STEREOSLIDER16)
+ shift = 16;
+
+ if (srec->full_scale)
+ side = BOTH;
+ else if (srec->right == NULL)
+ side = MONO;
+ else if (GTK_OBJECT (adjust) == srec->left)
+ side = LEFT;
+ else
+ side = RIGHT;
+
+ if (srec->mixext->type == MIXT_3D)
+ {
+#ifdef TEST_JOY
+#else
+ lval = 100 - (int) GTK_ADJUSTMENT (srec->left)->value;
+ rval = 360 - (int) GTK_ADJUSTMENT (srec->right)->value;
+ val = (50 << 8) | (lval & 0xff) | (rval << 16);
+ set_value (srec->mixext, val);
+#endif
+ return;
+ }
+
+ if (side == BOTH)
+ {
+ set_value (srec->mixext, val);
+ return;
+ }
+
+ if (side == MONO)
+ {
+ val = val | (val << shift);
+ set_value (srec->mixext, val);
+ return;
+ }
+
+ gang_on = 0;
+
+ if (srec->gang != NULL)
+ {
+ gang_on = GTK_TOGGLE_BUTTON (srec->gang)->active;
+ }
+
+ if (gang_on)
+ {
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->left), origval);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->right), origval);
+
+ val = val | (val << shift);
+ set_value (srec->mixext, val);
+
+ return;
+ }
+
+ lval = srec->mixext->maxvalue - (int) GTK_ADJUSTMENT (srec->left)->value;
+ rval = srec->mixext->maxvalue - (int) GTK_ADJUSTMENT (srec->right)->value;
+ val = lval | (rval << shift);
+ set_value (srec->mixext, val);
+}
+
+static void
+gang_change (GtkToggleButton * but, gpointer data)
+{
+ ctlrec_t *srec = (ctlrec_t *) data;
+ int val, aval, lval, rval;
+ int mask = 0xff, shift = 8;
+
+ if (!but->active)
+ return;
+
+ lval = srec->mixext->maxvalue - (int) GTK_ADJUSTMENT (srec->left)->value;
+ rval = srec->mixext->maxvalue - (int) GTK_ADJUSTMENT (srec->right)->value;
+ if (lval == rval)
+ return;
+
+ if (lval < rval)
+ lval = rval;
+
+ if (srec->mixext->type == MIXT_STEREOSLIDER16)
+ {
+ shift = 16;
+ mask = 0xffff;
+ }
+
+ val = lval | (lval << shift);
+ aval = srec->mixext->maxvalue - (val & mask);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->left), aval);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->right), aval);
+ set_value (srec->mixext, val);
+}
+
+/*ARGSUSED*/
+static void
+change_enum (GtkToggleButton * but, gpointer data)
+{
+ ctlrec_t *srec = (ctlrec_t *) data;
+ int val;
+ const char *entry;
+
+ entry = gtk_entry_get_text (GTK_ENTRY (srec->left));
+ if (*entry == 0) /* Empty - Why? */
+ return;
+
+ val = findenum (srec->mixext, entry);
+
+ set_value (srec->mixext, val);
+}
+
+
+static void
+change_on_off (GtkToggleButton * but, gpointer data)
+{
+ ctlrec_t *srec = (ctlrec_t *) data;
+ int val;
+
+ val = but->active;
+
+ /*
+ * old OSSv4 builds had a bug where SNDCTL_MIX_WRITE would always return
+ * 1 when changing a rec button.
+ */
+#if 0
+ val = set_value (srec->mixext, val);
+#else
+ set_value (srec->mixext, val);
+ val = get_value (srec->mixext);
+#endif
+ if (val != -1) but->active = val;
+}
+
+static void
+store_ctl (ctlrec_t * rec, int dev)
+{
+ rec->next = control_list[dev];
+ control_list[dev] = rec;
+}
+
+static void
+connect_scrollers (oss_mixext * thisrec, GtkObject * left, GtkObject * right,
+ GtkWidget * gang)
+{
+ ctlrec_t *srec;
+
+ srec = g_new (ctlrec_t, 1);
+ srec->mixext = thisrec;
+ srec->left = left;
+ srec->right = right;
+ srec->full_scale = (thisrec->type == MIXT_SLIDER);
+ srec->gang = gang;
+ gtk_signal_connect (GTK_OBJECT (left), "value_changed",
+ GTK_SIGNAL_FUNC (Scrolled), srec);
+ if (right != NULL)
+ gtk_signal_connect (GTK_OBJECT (right), "value_changed",
+ GTK_SIGNAL_FUNC (Scrolled), srec);
+ if (gang != NULL)
+ gtk_signal_connect (GTK_OBJECT (gang), "toggled",
+ GTK_SIGNAL_FUNC (gang_change), srec);
+
+ store_ctl (srec, thisrec->dev);
+
+}
+
+static void
+connect_peak (oss_mixext * thisrec, GtkWidget * left, GtkWidget * right)
+{
+ ctlrec_t *srec;
+
+ srec = g_new (ctlrec_t, 1);
+ srec->mixext = thisrec;
+ srec->left = GTK_OBJECT (left);
+ if (right == NULL)
+ srec->right = NULL;
+ else
+ srec->right = GTK_OBJECT (right);
+ srec->gang = NULL;
+ srec->last_left = 0;
+ srec->last_right = 0;
+
+ srec->next = peak_list[thisrec->dev];
+ peak_list[thisrec->dev] = srec;
+}
+
+static void
+connect_value_poll (oss_mixext * thisrec, GtkWidget * wid)
+{
+ ctlrec_t *srec;
+
+ srec = g_new (ctlrec_t, 1);
+ srec->mixext = thisrec;
+ srec->left = GTK_OBJECT (wid);
+ srec->right = NULL;
+ srec->gang = NULL;
+ srec->last_left = 0;
+ srec->last_right = 0;
+
+ srec->next = value_poll_list[thisrec->dev];
+ value_poll_list[thisrec->dev] = srec;
+}
+
+static void
+connect_enum (oss_mixext * thisrec, GtkObject * entry)
+{
+ ctlrec_t *srec;
+
+ srec = g_new (ctlrec_t, 1);
+ srec->mixext = thisrec;
+ srec->left = entry;
+ srec->right = NULL;
+ srec->gang = NULL;
+ gtk_signal_connect (entry, "changed", GTK_SIGNAL_FUNC (change_enum), srec);
+ store_ctl (srec, thisrec->dev);
+
+}
+
+static void
+connect_onoff (oss_mixext * thisrec, GtkObject * entry)
+{
+ ctlrec_t *srec;
+
+ srec = g_new (ctlrec_t, 1);
+ srec->mixext = thisrec;
+ srec->left = entry;
+ srec->right = NULL;
+ srec->gang = NULL;
+ gtk_signal_connect (entry, "toggled", GTK_SIGNAL_FUNC (change_on_off), srec);
+ store_ctl (srec, thisrec->dev);
+
+}
+
+/*
+ * Create notebook and populate it with multiple mixer tabs. Returns notebook.
+ */
+static GtkWidget *
+load_multiple_devs (void)
+{
+ int i, first_page = -1;
+ GtkWidget *notebook, *mixer_page, *label, *vbox, *hbox;
+
+ if (ioctl (global_fd, SNDCTL_MIX_NRMIX, &mixer_num) == -1)
+ {
+ perror ("SNDCTL_MIX_NRMIX");
+ exit (-1);
+ }
+
+ if (mixer_num > MAX_DEVS) mixer_num = MAX_DEVS;
+
+ /* This can happen when ossxmix is restarted by {get/set}_value */
+ if (dev > mixer_num - 1) dev = find_default_mixer ();
+
+ notebook = gtk_notebook_new ();
+ for (i = 0; i < mixer_num; i++)
+ {
+ if (get_fd(i) == -1) continue;
+ mixer_page = load_devinfo (i);
+ if (mixer_page == NULL) continue;
+ if (first_page == -1) first_page = i;
+ vbox = gtk_vbox_new (FALSE, 0);
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), mixer_page, FALSE, TRUE, 0);
+ gtk_box_pack_end (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+ gtk_widget_show (vbox);
+ if (root[i] == NULL)
+ {
+ fprintf (stderr, "No device root node for mixer %d\n", i);
+ exit (-1);
+ }
+ label = gtk_label_new (root[i]->name);
+ gtk_notebook_append_page (GTK_NOTEBOOK (notebook), vbox, label);
+ }
+
+ if (root[dev] != NULL) first_page = dev;
+ else if (first_page != -1) dev = first_page;
+ else
+ {
+ fprintf (stderr, "No mixers could be opened\n");
+ exit (EXIT_FAILURE);
+ }
+
+#ifndef GTK1_ONLY
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), dev);
+#else
+ gtk_notebook_set_page (GTK_NOTEBOOK (notebook), dev);
+#endif /* !GTK1_ONLY */
+ gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
+ gtk_signal_connect (GTK_OBJECT (notebook), "switch-page",
+ GTK_SIGNAL_FUNC (switch_page), (gpointer)poll_tag_list);
+ gtk_widget_show (notebook);
+
+ return notebook;
+}
+
+/*
+ * The load_devinfo() routine loads the mixer definitions and creates the
+ * GUI structure based on it.
+ *
+ * In short the algorithm is to create GTK vbox or hbox widgets for
+ * each group. A vbox is created for the root group. Then the orientation is
+ * changed in each level of sub-groups. However there are some exceptions to
+ * this rule (will be described in the documentation.
+ *
+ * The individual controls are just placed inside the hbox/vbx widgets of
+ * the parent groups. However the "legacy" mixer controls (before
+ * MIXT_MARKER) will be handled in slightly different way (please consult
+ * the documentation).
+ */
+static GtkWidget *
+load_devinfo (int dev)
+{
+ char tmp[1024], *name = '\0';
+ char ** extnames;
+ int i, n, val, left, right, mx, g, mask, shift;
+ int angle, vol;
+ int width;
+ int ngroups = 0;
+ int parent = 0;
+ int change_color;
+ oss_mixext *thisrec;
+ oss_mixerinfo mi;
+ GdkColor color;
+ GtkWidget *wid, *wid2, *gang, *rootwid = NULL, *pw = NULL, *frame, *box;
+ GtkWidget **widgets;
+ GtkObject *adjust, *adjust2;
+ gboolean change_orient = TRUE, ori, * orient;
+ gboolean expand, use_layout_b = FALSE;
+
+ mi.dev = dev;
+ if (ioctl (global_fd, SNDCTL_MIXERINFO, &mi) == -1)
+ {
+ perror ("SNDCTL_MIXERINFO");
+ exit (-1);
+ }
+
+ if (!mi.enabled) return NULL;
+ /* e.g. disconnected USB device */
+
+ if (mi.caps & MIXER_CAP_LAYOUT_B)
+ use_layout_b = TRUE;
+
+ if ((mi.caps & MIXER_CAP_NARROW) && (width_adjust >= 0))
+ width_adjust = -1;
+
+ n = mi.nrext;
+ if (n < 1)
+ {
+ fprintf (stderr, "Error: illogical number of extension info records\n");
+ return NULL;
+ }
+ extrec[dev] = g_new0 (oss_mixext, n+1);
+ extnames = g_new (char *, n+1);
+ widgets = g_new0 (GtkWidget *, n);
+ orient = g_new0 (gboolean, n);
+
+ for (i = 0; i < n; i++)
+ {
+ change_color = 0;
+ mask = 0xff;
+ shift = 8;
+ expand = TRUE;
+
+ thisrec = &extrec[dev][i];
+ thisrec->dev = dev;
+ thisrec->ctrl = i;
+
+ if (ioctl (get_fd(dev), SNDCTL_MIX_EXTINFO, thisrec) == -1)
+ {
+ if (errno == EINVAL)
+ printf ("Incompatible OSS version\n");
+ else
+ perror ("SNDCTL_MIX_EXTINFO");
+ exit (-1);
+ }
+
+ if (thisrec->id[0] == '-') /* Hidden one */
+ thisrec->id[0] = '\0';
+
+ if (thisrec->type == MIXT_STEREOSLIDER16
+ || thisrec->type == MIXT_MONOSLIDER16)
+ {
+ mask = 0xffff;
+ shift = 16;
+ }
+
+ if ((thisrec->type != MIXT_DEVROOT) && (thisrec->type != MIXT_MARKER))
+ {
+ parent = thisrec->parent;
+ name = cut_name (thisrec->id);
+ if ((thisrec->type == MIXT_GROUP) && !change_orient && (parent == 0))
+ pw = rootwid;
+ else
+ pw = widgets[parent];
+ if ((pw == NULL) && (show_all))
+ fprintf (stderr, "Control %d/%s: Parent(%d)==NULL\n", i,
+ thisrec->extname, parent);
+ }
+
+#if OSS_VERSION >= 0x040004
+ if (thisrec->rgbcolor != 0)
+ {
+ /*
+ * Pick the 8 bit RGB component colors and expand them to 16 bits
+ */
+ color.red = (thisrec->rgbcolor & 0xff0000) >> 8;
+ color.green = (thisrec->rgbcolor & 0x00ff00);
+ color.blue = (thisrec->rgbcolor & 0x0000ff) << 8;
+ change_color=1;
+
+ }
+#endif
+
+ switch (thisrec->type)
+ {
+ case MIXT_DEVROOT:
+ root[dev] = (oss_mixext_root *) & thisrec->data;
+ extnames[i] = g_strdup("");
+ rootwid = gtk_vbox_new (FALSE, 2);
+ gtk_box_set_child_packing (GTK_BOX (rootwid), rootwid, TRUE, TRUE,
+ 100, GTK_PACK_START);
+ wid = gtk_hbox_new (FALSE, 1);
+ gtk_box_pack_start (GTK_BOX (rootwid), wid, TRUE, TRUE, 1);
+ gtk_widget_show_all (rootwid);
+ widgets[i] = wid;
+ break;
+
+ case MIXT_GROUP:
+ if (!show_all)
+ break;
+#if OSS_VERSION >= 0x040090
+ if (!boomer_workaround) /* Boomer 4.0 doesn't provide update_counters */
+ if (thisrec->update_counter == 0)
+ break;
+#endif
+
+ if (*extnames[parent] == '\0')
+ strcpy (tmp, name);
+ else
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ if (thisrec->flags & MIXF_FLAT) /* Group contains only ENUM controls */
+ expand = FALSE;
+ ori = !orient[parent];
+ if (change_orient)
+ ori = !ori;
+ orient[i] = ori;
+
+ switch (ori)
+ {
+ case 0:
+ wid = gtk_vbox_new (FALSE, 1);
+ break;
+
+ default:
+ ngroups++;
+ if (!use_layout_b)
+ ori = !ori;
+ orient[i] = ori;
+ wid = gtk_hbox_new (FALSE, 1);
+ }
+
+ frame = gtk_frame_new (extnames[i]);
+ manage_label (frame, thisrec);
+ gtk_box_pack_start (GTK_BOX (pw), frame, expand, TRUE, 1);
+ gtk_container_add (GTK_CONTAINER (frame), wid);
+ gtk_widget_set_name (wid, extnames[i]);
+ gtk_widget_show_all (frame);
+ widgets[i] = wid;
+ {
+ int tmp = -1;
+
+ if ((sscanf (extnames[i], "vmix%d-out", &tmp) == 1) &&
+ (tmp >= 0))
+ {
+ create_update (NULL, NULL, NULL, wid, thisrec, WHAT_VMIX, n);
+ }
+ }
+ break;
+
+ case MIXT_HEXVALUE:
+ case MIXT_VALUE:
+ if (!show_all)
+ break;
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ val = get_value (thisrec);
+
+ wid = gtk_label_new ("????");
+ gtk_box_pack_start (GTK_BOX (pw), wid, FALSE, TRUE, 0);
+ if (thisrec->flags & MIXF_POLL)
+ connect_value_poll (thisrec, wid);
+ else
+ create_update (NULL, NULL, NULL, wid, thisrec, WHAT_UPDATE, i);
+ gtk_widget_set_name (wid, extnames[i]);
+ update_label (thisrec, wid, val);
+ check_tooltip(thisrec, wid);
+ gtk_widget_show (wid);
+ break;
+
+ case MIXT_STEREODB:
+ case MIXT_MONODB:
+ if (!show_all)
+ break;
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ wid = gtk_button_new_with_label (extnames[i]);
+ gtk_box_pack_start (GTK_BOX (pw), wid, FALSE, TRUE, 0);
+ gtk_widget_set_name (wid, extnames[i]);
+ check_tooltip(thisrec, wid);
+ gtk_widget_show (wid);
+ break;
+
+ case MIXT_ONOFF:
+#ifdef MIXT_MUTE
+ case MIXT_MUTE: /* TODO: Mute could have custom widget */
+#endif /* MIXT_MUTE */
+ if (!show_all)
+ break;
+ val = get_value (thisrec) & 0x01;
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ wid = gtk_check_button_new_with_label (extnames[i]);
+#ifndef GTK1_ONLY
+ if (change_color)
+ gtk_widget_modify_bg (wid, GTK_STATE_NORMAL, &color);
+#endif /* !GTK1_ONLY */
+ connect_onoff (thisrec, GTK_OBJECT (wid));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), val);
+ create_update (NULL, NULL, NULL, wid, thisrec, WHAT_UPDATE, 0);
+ gtk_box_pack_start (GTK_BOX (pw), wid, FALSE, TRUE, 0);
+ gtk_widget_set_name (wid, extnames[i]);
+ check_tooltip(thisrec, wid);
+ gtk_widget_show (wid);
+ break;
+
+ case MIXT_STEREOVU:
+ case MIXT_STEREOPEAK:
+ if (!show_all)
+ break;
+ val = get_value (thisrec);
+ mx = thisrec->maxvalue;
+ left = mx - (val & 0xff);
+ right = mx - ((val >> 8) & 0xff);
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ wid = gtk_vu_new ();
+ wid2 = gtk_vu_new ();
+ check_tooltip(thisrec, wid);
+
+ connect_peak (thisrec, wid, wid2);
+ gtk_widget_set_name (wid, extnames[i]);
+ gtk_widget_set_name (wid2, extnames[i]);
+ if (strcmp (extnames[parent], extnames[i]) != 0)
+ {
+ frame = gtk_frame_new (extnames[i]);
+ manage_label (frame, thisrec);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (pw), frame, FALSE, TRUE, 0);
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 1);
+ gtk_box_pack_start (GTK_BOX (box), wid2, TRUE, TRUE, 1);
+ gtk_widget_show_all (frame);
+ }
+ else
+ {
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_box_pack_start (GTK_BOX (pw), box, FALSE, TRUE, 1);
+ gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 1);
+ gtk_box_pack_start (GTK_BOX (box), wid2, TRUE, TRUE, 1);
+ gtk_widget_show_all (box);
+ }
+ break;
+
+ case MIXT_MONOVU:
+ case MIXT_MONOPEAK:
+ if (!show_all)
+ break;
+ val = get_value (thisrec);
+ mx = thisrec->maxvalue;
+ left = mx - (val & 0xff);
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ wid = gtk_vu_new ();
+ check_tooltip(thisrec, wid);
+
+ connect_peak (thisrec, wid, NULL);
+ gtk_widget_set_name (wid, extnames[i]);
+ if (strcmp (extnames[parent], extnames[i]) != 0)
+ {
+ frame = gtk_frame_new (extnames[i]);
+ manage_label (frame, thisrec);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (pw), frame, FALSE, TRUE, 0);
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 1);
+ gtk_widget_show_all (frame);
+ }
+ else
+ {
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_box_pack_start (GTK_BOX (pw), box, FALSE, TRUE, 1);
+ gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 1);
+ gtk_widget_show_all (box);
+ }
+ break;
+
+ case MIXT_STEREOSLIDER:
+ case MIXT_STEREOSLIDER16:
+ if (!show_all)
+ break;
+ width = -1;
+
+ if (width_adjust < 0)
+ width = 12;
+ val = get_value (thisrec);
+ mx = thisrec->maxvalue;
+ left = mx - (val & mask);
+ right = mx - ((val >> shift) & mask);
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ adjust = gtk_adjustment_new (left, 0, mx, 1, 5, 0);
+ adjust2 = gtk_adjustment_new (right, 0, mx, 1, 5, 0);
+ gang = gtk_check_button_new ();
+ g = (left == right);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gang), g);
+
+ connect_scrollers (thisrec, adjust, adjust2, gang);
+ create_update (NULL, adjust, adjust2, gang, thisrec, WHAT_UPDATE,
+ 0);
+
+ wid = gtk_vscale_new (GTK_ADJUSTMENT (adjust));
+ check_tooltip(thisrec, wid);
+#ifndef GTK1_ONLY
+ if (change_color)
+ gtk_widget_modify_bg (wid, GTK_STATE_NORMAL, &color);
+ gtk_widget_set_size_request (wid, width, 80);
+#endif /* !GTK1_ONLY */
+ gtk_scale_set_digits (GTK_SCALE (wid), 0);
+ gtk_scale_set_draw_value (GTK_SCALE (wid), FALSE);
+ gtk_widget_set_name (wid, extnames[i]);
+
+ wid2 = gtk_vscale_new (GTK_ADJUSTMENT (adjust2));
+#ifndef GTK1_ONLY
+ if (change_color)
+ gtk_widget_modify_bg (wid2, GTK_STATE_NORMAL, &color);
+ gtk_widget_set_size_request (wid2, width, 80);
+#endif /* !GTK1_ONLY */
+ gtk_scale_set_digits (GTK_SCALE (wid2), 0);
+ gtk_scale_set_draw_value (GTK_SCALE (wid2), FALSE);
+ gtk_widget_set_name (wid2, extnames[i]);
+
+ if (strcmp (extnames[parent], extnames[i]) != 0)
+ {
+ frame = gtk_frame_new (extnames[i]);
+ manage_label (frame, thisrec);
+ /* gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); */
+ gtk_box_pack_start (GTK_BOX (pw), frame, FALSE, FALSE, 1);
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_box_pack_start (GTK_BOX (box), wid, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), wid2, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), gang, FALSE, TRUE, 0);
+ gtk_widget_show_all (frame);
+ }
+ else
+ {
+ box = gtk_hbox_new (FALSE, 1);
+#if 1
+ gtk_box_pack_start (GTK_BOX (pw), box, TRUE, TRUE, 1);
+#else
+ gtk_box_pack_start (GTK_BOX (pw), box, FALSE, FALSE, 1);
+#endif
+ gtk_box_pack_start (GTK_BOX (box), wid, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), wid2, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), gang, FALSE, TRUE, 0);
+ gtk_widget_show_all (box);
+ }
+ break;
+
+ case MIXT_3D:
+#ifdef TEST_JOY
+ if (!show_all)
+ break;
+ val = get_value (thisrec);
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ wid = gtk_joy_new ();
+ check_tooltip(thisrec, wid);
+ create_update (NULL, NULL, NULL, wid, thisrec, WHAT_UPDATE, 0);
+
+ if (strcmp (extnames[parent], extnames[i]) != 0)
+ {
+ frame = gtk_frame_new (extnames[i]);
+ manage_label (frame, thisrec);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (pw), frame, FALSE, TRUE, 0);
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 1);
+ gtk_widget_show_all (frame);
+ }
+ else
+ {
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_box_pack_start (GTK_BOX (pw), box, FALSE, TRUE, 1);
+ gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 1);
+ gtk_widget_show_all (box);
+ }
+ break;
+#else
+ if (!show_all)
+ break;
+ val = get_value (thisrec);
+ mx = thisrec->maxvalue;
+ vol = 100 - (val & 0x00ff);
+ angle = 360 - ((val >> 16) & 0xffff);
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ adjust = gtk_adjustment_new (vol, 0, 100, 1, 5, 0);
+ adjust2 = gtk_adjustment_new (angle, 0, 360, 1, 5, 0);
+ connect_scrollers (thisrec, adjust, adjust2, NULL);
+ create_update (NULL, adjust, adjust2, NULL, thisrec, WHAT_UPDATE,
+ 0);
+ wid = gtk_vscale_new (GTK_ADJUSTMENT (adjust));
+ gtk_scale_set_digits (GTK_SCALE (wid), 0);
+ gtk_scale_set_draw_value (GTK_SCALE (wid), FALSE);
+ gtk_widget_set_name (wid, extnames[i]);
+ wid2 = gtk_vscale_new (GTK_ADJUSTMENT (adjust2));
+#ifndef GTK1_ONLY
+ if (change_color)
+ {
+ gtk_widget_modify_bg (wid, GTK_STATE_NORMAL, &color);
+ gtk_widget_modify_bg (wid2, GTK_STATE_NORMAL, &color);
+ }
+#endif /* !GTK1_ONLY */
+ gtk_scale_set_digits (GTK_SCALE (wid2), 0);
+ gtk_scale_set_draw_value (GTK_SCALE (wid2), FALSE);
+ gtk_widget_set_name (wid2, extnames[i]);
+
+ frame = gtk_frame_new (extnames[i]);
+ manage_label (frame, thisrec);
+ /* gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); */
+ gtk_box_pack_start (GTK_BOX (pw), frame, FALSE, FALSE, 1);
+ box = gtk_hbox_new (FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (frame), box);
+ gtk_box_pack_start (GTK_BOX (box), wid, FALSE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), wid2, TRUE, TRUE, 0);
+ gtk_widget_show_all (frame);
+ break;
+#endif
+
+ case MIXT_MONOSLIDER:
+ case MIXT_MONOSLIDER16:
+ case MIXT_SLIDER:
+ if (!show_all)
+ break;
+ val = get_value (thisrec);
+ mx = thisrec->maxvalue;
+
+ if (thisrec->type == MIXT_MONOSLIDER)
+ val &= 0xff;
+ else if (thisrec->type == MIXT_MONOSLIDER16)
+ val &= 0xffff;
+
+ val = mx - val;
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ adjust = gtk_adjustment_new (val, 0, mx, 1, 5, 0);
+ connect_scrollers (thisrec, adjust, NULL, NULL);
+ create_update (NULL, adjust, NULL, NULL, thisrec, WHAT_UPDATE, 0);
+ wid = gtk_vscale_new (GTK_ADJUSTMENT (adjust));
+ check_tooltip(thisrec, wid);
+#ifndef GTK1_ONLY
+ if (change_color)
+ gtk_widget_modify_bg (wid, GTK_STATE_NORMAL, &color);
+ gtk_widget_set_size_request (wid, -1, 80);
+#endif /* !GTK1_ONLY */
+ gtk_scale_set_digits (GTK_SCALE (wid), 0);
+ gtk_scale_set_draw_value (GTK_SCALE (wid), FALSE);
+ gtk_widget_set_name (wid, extnames[i]);
+
+ if (strcmp (extnames[parent], extnames[i]) != 0)
+ {
+ frame = gtk_frame_new (extnames[i]);
+ manage_label (frame, thisrec);
+ /* gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); */
+ gtk_box_pack_start (GTK_BOX (pw), frame, FALSE, FALSE, 1);
+ gtk_container_add (GTK_CONTAINER (frame), wid);
+ gtk_widget_show_all (frame);
+ }
+ else
+ {
+ gtk_box_pack_start (GTK_BOX (pw), wid, FALSE, FALSE, 1);
+ gtk_widget_show (wid);
+ }
+ break;
+
+ case MIXT_ENUM:
+
+ if (!show_all)
+ break;
+ if (*thisrec->id == 0)
+ extnames[i] = extnames[parent];
+ else
+ {
+ snprintf (tmp, sizeof(tmp), "%s.%s", extnames[parent], name);
+ store_name (dev, i, tmp, extnames);
+ }
+ val = get_value (thisrec) & 0xff;
+
+ wid = gtk_combo_new ();
+ check_tooltip(thisrec, wid);
+#ifndef GTK1_ONLY
+ if (change_color)
+ gtk_widget_modify_fg (wid, GTK_STATE_NORMAL, &color);
+#endif /* !GTK1_ONLY */
+ {
+ GList *opt = NULL;
+
+ if (!(thisrec->flags & MIXF_WIDE))
+ gtk_widget_set_usize (wid, 100 + 20 * width_adjust, -1);
+ opt = load_enum_values (extnames[i], thisrec);
+ gtk_combo_set_popdown_strings (GTK_COMBO (wid), opt);
+ g_list_free (opt);
+
+ gtk_combo_set_use_arrows_always (GTK_COMBO (wid), 1);
+ gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (wid)->entry),
+ FALSE);
+ }
+ connect_enum (thisrec, GTK_OBJECT (GTK_COMBO (wid)->entry));
+ create_update (NULL, NULL, NULL, wid, thisrec, WHAT_UPDATE, i);
+ gtk_widget_set_name (wid, extnames[i]);
+ frame = gtk_frame_new (extnames[i]);
+#ifndef GTK1_ONLY
+ if (change_color)
+ gtk_widget_modify_bg (wid, GTK_STATE_NORMAL, &color);
+#endif /* !GTK1_ONLY */
+ manage_label (frame, thisrec);
+ gtk_box_pack_start (GTK_BOX (pw), frame, TRUE, FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (frame), wid);
+ gtk_widget_show_all (frame);
+ break;
+
+ case MIXT_MARKER:
+ show_all = 1;
+ change_orient = FALSE;
+ break;
+
+ default:
+ fprintf (stderr, "Unknown type for control %s\n", thisrec->extname);
+ }
+
+ }
+
+ g_free (extnames);
+ g_free (widgets);
+ g_free (orient);
+ return rootwid;
+}
+
+/*
+ * Creates the widget tree. Returns dimensions of scrolledwin.
+ */
+static GtkRequisition
+create_widgets (void)
+{
+ GtkRequisition Dimensions;
+ char tmp[100];
+
+ scrolledwin = gtk_scrolled_window_new (NULL, NULL);
+ /*
+ * A GtkScrolledWindow placed inside a GtkWindow is not considered to have
+ * a demand for space on its parent if it's policy is GTK_POLICY_AUTOMATIC.
+ * So if the window is realized, if will be reduced to the
+ * GtkScrolledWindow's minimum size which is quite small.
+ *
+ * To get around this, we setup scrolledwin with GTK_POLICY_NEVER, get the
+ * size GTK would have used for the window in that case, and use it as a
+ * default size for the window after we've reset the policy to
+ * GTK_POLICY_AUTOMATIC.
+ */
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
+ GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+ gtk_container_add (GTK_CONTAINER (window), scrolledwin);
+ if (!load_all_devs)
+ {
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwin),
+ load_devinfo (dev));
+ if (root[dev] == NULL)
+ {
+ fprintf (stderr, "No device root node\n");
+ exit (-1);
+ }
+ snprintf (tmp, sizeof(tmp), "ossxmix - device %d / %s",
+ dev, root[dev]->name);
+ gtk_window_set_title (GTK_WINDOW (window), tmp);
+ gtk_widget_size_request (scrolledwin, &Dimensions);
+ }
+ else
+ {
+ GtkWidget * notebook, * tab;
+ GtkRequisition tDimensions;
+
+ notebook = load_multiple_devs ();
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolledwin),
+ notebook);
+ gtk_window_set_title (GTK_WINDOW (window), "ossxmix");
+ gtk_widget_size_request (scrolledwin, &Dimensions);
+
+ /*
+ * Dimensions.height doesn't include the tab for some reason.
+ * I hate GTK.
+ */
+ tab = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook),
+ gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook),
+ gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook))));
+ gtk_widget_size_request (tab, &tDimensions);
+ Dimensions.height += tDimensions.height;
+ }
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_show (scrolledwin);
+ return Dimensions;
+}
+
+/*
+ * Reload the entire GUI
+ */
+static void
+reload_gui (void)
+{
+#define FREECTL(x) do { \
+ for (i=0; i < MAX_DEVS; i++) \
+ { \
+ for (p = x[i]; p != NULL;) \
+ { \
+ nextp = p->next; \
+ g_free (p); \
+ p = nextp; \
+ } \
+ x[i] = NULL; \
+ } \
+ } while (0)
+
+ ctlrec_t * p, * nextp;
+ int i;
+
+ remove_timeout ((gpointer)poll_tag_list);
+ fully_started = 0;
+ FREECTL (control_list); FREECTL (peak_list);
+ FREECTL (check_list); FREECTL (value_poll_list);
+ for (i=0; i < MAX_DEVS; i++)
+ {
+ root[i] = NULL;
+ g_free (extrec[i]);
+ extrec[i] = NULL;
+ if (local_fd[i] != -1)
+ {
+ close (local_fd[i]);
+ local_fd[i] = -1;
+ }
+ }
+
+ gtk_widget_destroy (scrolledwin);
+ create_widgets ();
+ fully_started = 1;
+ add_timeout ((gpointer)poll_tag_list);
+#undef FREECTL
+}
+
+/*
+ * The update_label() routine is used to update the values of certain
+ * read only controls.
+ */
+
+static void
+update_label (oss_mixext * mixext, GtkWidget * wid, int val)
+{
+ char tmp[100];
+
+ if (mixext->type == MIXT_HEXVALUE)
+ snprintf (tmp, sizeof(tmp), "[%s: 0x%x] ",
+ gtk_widget_get_name (wid), val);
+ else
+ snprintf (tmp, sizeof(tmp), "[%s: %d] ",
+ gtk_widget_get_name (wid), val);
+
+ if (mixext->flags & MIXF_HZ)
+ {
+ if (val > 1000000)
+ {
+ snprintf (tmp, sizeof(tmp), "[%s: %d.%03d MHz] ",
+ gtk_widget_get_name (wid), val / 1000000,
+ (val / 1000) % 1000);
+ }
+ else if (val > 1000)
+ {
+ snprintf (tmp, sizeof(tmp), "[%s: %d.%03d kHz] ",
+ gtk_widget_get_name (wid), val / 1000, val % 1000);
+ }
+ else
+ snprintf (tmp, sizeof(tmp), "[%s: %d Hz] ",
+ gtk_widget_get_name (wid), val);
+ }
+ else if (mixext->flags & MIXF_OKFAIL)
+ {
+ if (val != 0)
+ snprintf (tmp, sizeof(tmp), "[%s: Ok] ",
+ gtk_widget_get_name (wid));
+ else
+ snprintf (tmp, sizeof(tmp), "[%s: Fail] ",
+ gtk_widget_get_name (wid));
+ }
+ gtk_label_set (GTK_LABEL (wid), tmp);
+}
+
+/*
+ * The do_update() routine reads a value of certain mixer control
+ * and updates the on-screen value depending on the type of the control.
+ */
+
+static void
+do_update (ctlrec_t * srec)
+{
+ int val, mx, left, right, vol, angle;
+ int mask = 0xff, shift = 8;
+
+ val = get_value (srec->mixext);
+ if (val == -1) return;
+
+ if (srec->mixext->type == MIXT_MONOSLIDER16
+ || srec->mixext->type == MIXT_STEREOSLIDER16)
+ {
+ mask = 0xffff;
+ shift = 16;
+ }
+
+ switch (srec->mixext->type)
+ {
+ case MIXT_ONOFF:
+#ifdef MIXT_MUTE
+ case MIXT_MUTE:
+#endif /* MIXT_MUTE */
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (srec->gang), val);
+ break;
+
+ case MIXT_ENUM:
+ gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (srec->gang)->entry),
+ showenum (srec->mixext, val));
+ break;
+
+ case MIXT_VALUE:
+ case MIXT_HEXVALUE:
+ update_label (srec->mixext, (srec->gang), val);
+ break;
+
+ case MIXT_SLIDER:
+ mx = srec->mixext->maxvalue;
+ val = mx - val;
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->left), val);
+ break;
+
+ case MIXT_MONOSLIDER:
+ case MIXT_MONOSLIDER16:
+ mx = srec->mixext->maxvalue;
+ val = mx - (val & mask);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->left), val);
+ break;
+
+ case MIXT_STEREOSLIDER:
+ case MIXT_STEREOSLIDER16:
+ mx = srec->mixext->maxvalue;
+ left = mx - (val & mask);
+ right = mx - ((val >> shift) & mask);
+ if (srec->gang != NULL)
+ if (left != right)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (srec->gang), 0);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->left), left);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->right), right);
+ break;
+
+ case MIXT_3D:
+#ifdef TEST_JOY
+ if (srec->gang != NULL)
+ gtk_joy_set_level (GTK_JOY (srec->gang), val);
+#else
+ vol = 100 - (val & 0x00ff);
+ angle = 360 - ((val >> 16) & 0xffff);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->left), vol);
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (srec->right), angle);
+#endif
+ break;
+ }
+}
+
+/*
+ * The poll_all() routine get's called reqularily. It checks the
+ * modify counter for the mixer by calling {!nlink SNDCTL_MIXERINFO}.
+ * It checks if some other mixer program has made changes to the settings
+ * by comparing the modify counter against the "expected" value.
+ *
+ * If the mixer was changed then all the controls will be reloaded and updated.
+ */
+
+/*ARGSUSED*/
+static gint
+poll_all (gpointer data)
+{
+ ctlrec_t *srec;
+ oss_audioinfo ainfo;
+ char new_label[FRAME_NAME_LENGTH+1] = "";
+ int status_changed = 0;
+ oss_mixerinfo inf;
+
+ inf.dev = dev;
+ if (ioctl (global_fd, SNDCTL_MIXERINFO, &inf) == -1)
+ {
+ perror ("SNDCTL_MIXERINFO");
+ exit (-1);
+ }
+ if (!inf.enabled)
+ {
+ reload_gui ();
+ return TRUE;
+ }
+
+/*
+ * Compare the modify counter.
+ */
+ if ((inf.modify_counter - prev_update_counter[dev]) > set_counter[dev])
+ status_changed = 1;
+ prev_update_counter[dev] = inf.modify_counter;
+ set_counter[dev] = 0;
+
+ srec = check_list[dev];
+
+ while (srec != NULL)
+ {
+ switch (srec->what_to_do)
+ {
+ case WHAT_LABEL:
+/*
+ * Names of certain mixer controls depend on the application that is using
+ * the associated audio device. Handling for this is here
+ */
+ ainfo.dev = srec->parm;
+ if (ioctl (global_fd, SNDCTL_ENGINEINFO, &ainfo) == -1)
+ {
+ perror ("SNDCTL_ENGINEINFO");
+ continue;
+ }
+ if (*ainfo.label != '\0')
+ {
+ strncpy (new_label, ainfo.label, FRAME_NAME_LENGTH);
+ new_label[FRAME_NAME_LENGTH] = '\0';
+ }
+ else
+ {
+ snprintf (new_label, FRAME_NAME_LENGTH, "pcm%d", srec->parm);
+ }
+ if ((srec->frame != NULL) &&
+ (strncmp (srec->frame_name, new_label, FRAME_NAME_LENGTH)))
+ {
+ strcpy (srec->frame_name, new_label);
+ gtk_frame_set_label (GTK_FRAME (srec->frame), new_label);
+ }
+ break;
+ case WHAT_VMIX:
+/*
+ * The aforementioned mixer controls can be create dynamically, so ossxmix
+ * needs to poll for this. Handling for this is here
+ */
+ if (inf.nrext != srec->parm)
+ {
+ srec->parm = inf.nrext;
+/*
+ * Since we know the added controls are vmix controls, we should be able to do
+ * something more graceful here, like reloading only the current device, or
+ * even adding the controls directly. This will do for now.
+ */
+ reload_gui ();
+ return TRUE;
+ }
+ break;
+ case WHAT_UPDATE:
+ if (status_changed)
+ do_update (srec);
+ break;
+ }
+ srec = srec->next;
+ }
+ return TRUE;
+}
+
+/*
+ * The poll_peaks() routine gets called several times per second to update the
+ * VU/peak meter LED bar widgets.
+ */
+
+/*ARGSUSED*/
+static gint
+poll_peaks (gpointer data)
+{
+ ctlrec_t *srec;
+ int val, left, right;
+
+ srec = peak_list[dev];
+
+ while (srec != NULL)
+ {
+ val = get_value (srec->mixext);
+ if (val == -1) return TRUE;
+
+ left = val & 0xff;
+ right = (val >> 8) & 0xff;
+
+ if (left > srec->last_left)
+ srec->last_left = left;
+
+ if (right > srec->last_right)
+ srec->last_right = right;
+
+ left = srec->last_left;
+ right = srec->last_right;
+
+ /* gtk_adjustment_set_value(GTK_ADJUSTMENT(srec->left), left);
+ gtk_adjustment_set_value(GTK_ADJUSTMENT(srec->right), right); */
+ gtk_vu_set_level (GTK_VU (srec->left),
+ (left * 8) / srec->mixext->maxvalue);
+
+ if (srec->right != NULL)
+ gtk_vu_set_level (GTK_VU (srec->right),
+ (right * 8) / srec->mixext->maxvalue);
+
+
+ if (srec->last_left > 0)
+ srec->last_left -= PEAK_DECAY;
+ if (srec->last_right > 0)
+ srec->last_right -= PEAK_DECAY;
+
+ srec = srec->next;
+ }
+
+ return TRUE;
+}
+
+/*ARGSUSED*/
+static gint
+poll_values (gpointer data)
+{
+ ctlrec_t *srec;
+ int val;
+
+ srec = value_poll_list[dev];
+
+ while (srec != NULL)
+ {
+ val = get_value (srec->mixext);
+ if (val == -1) return TRUE;
+
+ update_label (srec->mixext, GTK_WIDGET (srec->left), val);
+
+ srec = srec->next;
+ }
+
+ return TRUE;
+}
+
+/*ARGSUSED*/
+static gint
+poll_mixnum (gpointer data)
+{
+ int c_mixer_num;
+
+ if (ioctl (global_fd, SNDCTL_MIX_NRMIX, &c_mixer_num) == -1)
+ {
+ perror ("SNDCTL_MIX_NRMIX");
+ exit (-1);
+ }
+
+ if (c_mixer_num > MAX_DEVS) c_mixer_num = MAX_DEVS;
+ if (c_mixer_num != mixer_num) reload_gui ();
+
+ return TRUE;
+}
+
+static int
+find_default_mixer (void)
+{
+ oss_mixerinfo mi;
+ int i, best = -1, bestpri = 0, mix_num;
+
+ if (ioctl (global_fd, SNDCTL_MIX_NRMIX, &mix_num) == -1)
+ {
+ perror ("SNDCTL_MIX_NRMIX");
+ if (errno == EINVAL)
+ fprintf (stderr, "Error: OSS version 4.0 or later is required\n");
+ exit (-1);
+ }
+
+ if (mix_num == 0)
+ {
+ fprintf (stderr, "No mixers are available\n");
+ exit (-1);
+ }
+
+ for (i = 0; i < mix_num; i++)
+ {
+ mi.dev = i;
+
+ if (ioctl (global_fd, SNDCTL_MIXERINFO, &mi) == -1)
+ continue; /* Ignore errors */
+
+ if (mi.enabled)
+ {
+ if (best == -1) best = i;
+
+ if (mi.priority > bestpri)
+ {
+ best = i;
+ bestpri = mi.priority;
+ }
+ }
+ }
+
+ if (best == -1)
+ {
+ fprintf (stderr, "No mixers are available for use as a default mixer\n");
+ exit (-1);
+ }
+
+ return best;
+}
+
+static void
+parse_dimarg (const char * dimarg, GtkRequisition * Dimensions)
+{
+ long height = 0, width = 0;
+ char * p;
+
+ errno = 0;
+ width = strtol (dimarg, &p, 10);
+ if (errno || (width <= 0)) return;
+ if (width > Dimensions->width) width = Dimensions->width;
+ height = width;
+ if (*p != '\0')
+ {
+ errno = 0;
+ height = strtol (p+1, NULL, 10);
+ if (errno || (height <= 0)) height = width;
+ }
+
+ Dimensions->width = width;
+ if (height < Dimensions->height) Dimensions->height = height;
+ return;
+}
+
+int
+main (int argc, char **argv)
+{
+ extern char * optarg;
+ char * dimarg = NULL;
+ int i, v, c;
+ GtkRequisition Dimensions;
+#ifndef GTK1_ONLY
+ GdkPixbuf *icon_pix;
+#else
+ GdkPixmap *icon_pix;
+ GdkBitmap *icon_mask;
+#endif /* !GTK1_ONLY */
+
+ const char *devmixer;
+ oss_sysinfo si;
+
+ for (i=0; i< MAX_DEVS; i++)
+ local_fd[i] = -1; /* Not opened */
+
+ if ((devmixer=getenv("OSS_MIXERDEV"))==NULL)
+ devmixer = "/dev/mixer";
+
+#if !defined(GTK1_ONLY) && defined(DEBUG)
+ g_mem_set_vtable (glib_mem_profiler_table);
+#endif
+ /* Get Gtk to process the startup arguments */
+ gtk_init (&argc, &argv);
+
+ while ((c = getopt (argc, argv, "Sbd:g:hn:w:x")) != EOF)
+ switch (c)
+ {
+ case 'd':
+ dev = atoi (optarg);
+ load_all_devs = 0;
+ break;
+
+ case 'w':
+ v = 0;
+ v = atoi (optarg);
+ if (v <= 0)
+ v = 1;
+ width_adjust += v;
+ break;
+
+ case 'n':
+ v = 0;
+ v = atoi (optarg);
+ if (v <= 0)
+ v = 1;
+ width_adjust -= v;
+ break;
+
+ case 'x':
+ show_all = 0;
+ break;
+
+ case 'b':
+ background = 1;
+ break;
+
+ case 'S':
+ show_status_icon = 0;
+ break;
+
+ case 'g':
+ dimarg = optarg;
+ break;
+
+ case 'h':
+ printf ("Usage: %s [options...]\n", argv[0]);
+ printf (" -h Prints help (this screen)\n");
+ printf (" -d<dev#> Selects the mixer device\n");
+ printf (" -x Hides the \"legacy\" mixer controls\n");
+ printf (" -w[val] Make mixer bit wider on screen\n");
+ printf (" -n[val] Make mixer bit narrower on screen\n");
+ printf (" -b Start mixer in background\n");
+ printf (" -g[w:h] Start mixer window with w:h size\n");
+#ifdef STATUSICON
+ printf (" -S Don't place an icon in system tray\n");
+#endif /* STATUSICON */
+ exit (0);
+ break;
+ }
+
+ if (width_adjust < -2)
+ width_adjust = -2;
+ if (width_adjust > 4)
+ width_adjust = 4;
+
+ if ((global_fd = open (devmixer, O_RDWR, 0)) == -1)
+ {
+ perror (devmixer);
+ exit (-1);
+ }
+
+ atexit (cleanup);
+
+ if (ioctl(global_fd, SNDCTL_SYSINFO, &si) != -1)
+ if (si.versionnum == 0x040003) /* Boomer 4.0 */
+ boomer_workaround = 1;
+
+ if (dev == -1)
+ dev = find_default_mixer ();
+
+ v = chdir ("/"); /* We don't really care if this fails */
+
+ /* Create the app's main window */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ Dimensions = create_widgets ();
+ if (dimarg != NULL) parse_dimarg (dimarg, &Dimensions);
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ Dimensions.width, Dimensions.height);
+
+ fully_started = 1;
+
+ /* Connect a window's signal to a signal function */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (close_request), NULL);
+
+#ifndef GTK1_ONLY
+ icon_pix = gdk_pixbuf_new_from_xpm_data ((const char **)ossxmix);
+#ifdef STATUSICON
+ if (show_status_icon)
+ {
+ char tmp[100];
+
+ status_icon = gtk_status_icon_new_from_pixbuf (icon_pix);
+ snprintf (tmp, sizeof(tmp), "ossxmix - device %d / %s",
+ dev, root[dev]->name);
+ gtk_status_icon_set_tooltip (status_icon, tmp);
+ g_signal_connect (G_OBJECT (status_icon), "popup-menu",
+ G_CALLBACK (trayicon_popupmenu), NULL);
+ g_signal_connect (G_OBJECT (status_icon), "activate",
+ G_CALLBACK (activate_mainwindow), NULL);
+ }
+#endif /* STATUSICON */
+ gtk_window_set_icon (GTK_WINDOW (window), icon_pix);
+
+ g_signal_connect (G_OBJECT (window), "window-state-event",
+ G_CALLBACK (manage_timeouts), (gpointer)poll_tag_list);
+ if ((!background) || (!show_status_icon))
+ {
+ gtk_widget_show (window);
+ if (background) gtk_window_iconify (GTK_WINDOW (window));
+ }
+#if GTK_CHECK_VERSION(2,2,0)
+ else gdk_notify_startup_complete ();
+#endif
+#else
+ add_timeout ((gpointer)poll_tag_list);
+ gtk_widget_show (window);
+ if (background) XIconifyWindow (GDK_WINDOW_XDISPLAY (window->window),
+ GDK_WINDOW_XWINDOW (window->window),
+ DefaultScreen (GDK_DISPLAY ()));
+ icon_pix = gdk_pixmap_create_from_xpm_d (window->window, &icon_mask,
+ &window->style->bg[GTK_STATE_NORMAL],
+ (gchar **)ossxmix);
+ gdk_window_set_icon (window->window, NULL, icon_pix, icon_mask);
+#endif /* !GTK1_ONLY */
+
+ gtk_main ();
+
+ return 0;
+}
+
+/*
+ * Function to run when program is shutting down
+ */
+static void
+cleanup (void)
+{
+ int i;
+
+ close (global_fd);
+
+ for (i=0;i<MAX_DEVS;i++)
+ if (local_fd[i] != -1)
+ close (local_fd[i]);
+
+#if !defined(GTK1_ONLY) && GTK_CHECK_VERSION(2,2,0)
+ gdk_notify_startup_complete ();
+#endif /* !GTK1_ONLY */
+
+#ifdef DEBUG
+ g_mem_profile ();
+#endif
+}
+
+/*
+ * Function to handle a close signal on the window
+ */
+/*ARGSUSED*/
+static gint
+close_request (GtkWidget * theWindow, gpointer data)
+{
+#ifdef STATUSICON
+ if (show_status_icon && (gtk_status_icon_is_embedded (status_icon) == TRUE))
+ {
+ gtk_widget_hide (window);
+ return TRUE;
+ }
+#endif
+ gtk_main_quit ();
+ return FALSE;
+}
+
+/*
+ * Function to make sure only the currently shown mixer is polled
+ */
+/*ARGSUSED*/
+static void
+switch_page (GtkNotebook * notebook, GtkNotebookPage * page,
+ guint page_num, gpointer data)
+{
+#ifdef STATUSICON
+ char tmp[100];
+
+ if ((show_status_icon) &&
+ (gtk_status_icon_is_embedded (status_icon) == TRUE))
+ {
+ snprintf (tmp, sizeof(tmp), "ossxmix - device %d / %s",
+ page_num, root[page_num]->name);
+ gtk_status_icon_set_tooltip (status_icon, tmp);
+ }
+#endif /* STATUSICON */
+
+ /*
+ * GTK1 calls switch_page when scrolledwin is destroyed in reload_gui.
+ * This is merely annoying, but this check prevents it nonetheless.
+ */
+ if (fully_started == 0) return;
+ remove_timeout (data);
+ dev = page_num;
+ add_timeout (data);
+}
+
+/*
+ * Function to start polling mixer 'dev'
+ */
+/*ARGSUSED*/
+static gint
+add_timeout (gpointer data)
+{
+ guint *poll_tag_list = (guint *) data;
+
+ if ((peak_list[dev] != NULL) && (poll_tag_list[0] == 0))
+ poll_tag_list[0] = g_timeout_add (PEAK_POLL_INTERVAL, poll_peaks, NULL);
+ if ((value_poll_list[dev] != NULL) && (poll_tag_list[1] == 0))
+ poll_tag_list[1] = g_timeout_add (VALUE_POLL_INTERVAL, poll_values, NULL);
+ if (poll_tag_list[2] == 0)
+ poll_tag_list[2] = g_timeout_add (MIXER_POLL_INTERVAL, poll_all, NULL);
+ if ((poll_tag_list[3] == 0) && (load_all_devs))
+ poll_tag_list[3] = g_timeout_add (MIXNUM_POLL_INTERVAL, poll_mixnum, NULL);
+ return FALSE;
+}
+
+/*
+ * Function to stop polling mixer 'dev'
+ */
+/*ARGSUSED*/
+static gint
+remove_timeout (gpointer data)
+{
+ guint *poll_tag_list = (guint *) data;
+
+ if (poll_tag_list[0] != 0)
+ {
+ g_source_remove (poll_tag_list[0]);
+ poll_tag_list[0] = 0;
+ }
+ if (poll_tag_list[1] != 0)
+ {
+ g_source_remove (poll_tag_list[1]);
+ poll_tag_list[1] = 0;
+ }
+ if (poll_tag_list[2] != 0)
+ {
+ g_source_remove (poll_tag_list[2]);
+ poll_tag_list[2] = 0;
+ }
+ if (poll_tag_list[3] != 0)
+ {
+ g_source_remove (poll_tag_list[3]);
+ poll_tag_list[3] = 0;
+ }
+ return FALSE;
+}
+
+#ifndef GTK1_ONLY
+/*
+ * Function to make sure polling isn't done when window is minimized or hidden
+ */
+/*ARGSUSED*/
+static gint
+manage_timeouts (GtkWidget * w, GdkEventWindowState * e, gpointer data)
+{
+ if (e->new_window_state &
+ (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_WITHDRAWN))
+ {
+ remove_timeout (data);
+ return FALSE;
+ }
+ add_timeout (data);
+ return FALSE;
+}
+#endif /* !GTK1_ONLY */
+
+#ifdef STATUSICON
+/*ARGSUSED*/
+static void
+activate_mainwindow (GtkStatusIcon * icon, guint button, guint etime,
+ gpointer data)
+{
+ if (GTK_WIDGET_VISIBLE (window)) gtk_widget_hide (window);
+ else popup_mainwindow (NULL, NULL);
+}
+
+/*ARGSUSED*/
+static void
+popup_mainwindow (GtkWidget * w, gpointer data)
+{
+ gtk_widget_show (window);
+ gtk_window_present (GTK_WINDOW (window));
+}
+
+/*
+ * Popup menu when clicking on status icon
+ */
+/*ARGSUSED*/
+static void
+trayicon_popupmenu (GtkStatusIcon * icon, guint button, guint etime,
+ gpointer data)
+{
+ static GtkWidget *tray_menu = NULL;
+
+ if (tray_menu == NULL)
+ {
+ GtkWidget *item;
+ tray_menu = gtk_menu_new ();
+
+ item = gtk_menu_item_new_with_label ("Restore");
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (popup_mainwindow), NULL);
+ gtk_menu_append (tray_menu, item);
+ item = gtk_menu_item_new_with_label ("Quit");
+ gtk_menu_append (tray_menu, item);
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (gtk_main_quit), NULL);
+ }
+
+ gtk_widget_show_all (tray_menu);
+
+ gtk_menu_popup (GTK_MENU (tray_menu), NULL, NULL,
+ gtk_status_icon_position_menu, status_icon,
+ button, etime);
+}
+#endif /* STATUSICON */
+
diff --git a/cmd/ossxmix/ossxmix.man b/cmd/ossxmix/ossxmix.man
new file mode 100644
index 0000000..31b74e0
--- /dev/null
+++ b/cmd/ossxmix/ossxmix.man
@@ -0,0 +1,42 @@
+NAME
+ossxmix - Open Sound System GTK based GUI mixer program.
+
+SYNOPSIS
+ossxmix [-Sbhx] [-d <dev#>] [-w <value>] [-n <value>]
+
+DESCRIPTION
+ossxmix is a GTK+ based mixer applet that is used to display the
+mixer settings of physical and virtual audio devices. There can be
+several physical mixers for a single audio device. Mixers found on
+audio devices are controllers that set the volume, select the input,
+perform certain functions on the speakers or set various device
+characteristics.
+
+OPTIONS
+-h Display usage instructions.
+-d<dev#> Display only mixer device dev#. If this option is not given then
+ ossxmix will display all the mixers available on the system.
+-x Hides the "legacy" mixer controls.
+-w[value] Make the mixer slightly wider on the screen. This option affects
+ only the selection boxes and peak meter LED bars. In most cases
+ this option is not required. The value can be between 1 (default)
+ and 4.
+-n[value] Make the mixer slightly narrower than normally. This may be
+ necessary if the mixer is wider than the screen. The value can be
+ 1 (default) or 2.
+-g[w:h] Start ossxmix window with dimnesions w:h.
+-b Start in background.
+-S Do not try to place an icon in the system tray.
+
+NOTES
+o ossxmix without the -d parameter shows all the currently enabled mixers.
+o Standard gtk toolkit options like --display are available as well.
+
+SEE ALSO
+ossdevlinks(1), ossmix(1), savemixer(1)
+
+FILES
+/usr/bin/ossxmix
+
+AUTHOR
+4Front Technologies
diff --git a/cmd/ossxmix/ossxmix.xpm b/cmd/ossxmix/ossxmix.xpm
new file mode 100644
index 0000000..3c27a6e
--- /dev/null
+++ b/cmd/ossxmix/ossxmix.xpm
@@ -0,0 +1,312 @@
+/* XPM */
+static const char *ossxmix[] = {
+/* width height num_colors chars_per_pixel */
+" 121 51 254 2",
+/* colors */
+".. c #020202",
+".# c #36e7fe",
+".a c #0ac2fe",
+".b c #0481cc",
+".c c #fe6666",
+".d c #025690",
+".e c #02426d",
+".f c #6c110d",
+".g c #0e3a46",
+".h c #e65856",
+".i c #f6f6f6",
+".j c #a2a2a2",
+".k c #66120e",
+".l c #06344c",
+".m c #62120e",
+".n c #cecece",
+".o c #022e4e",
+".p c #7a7a7a",
+".q c #eaeaea",
+".r c #8a8a8a",
+".s c #022a44",
+".t c #76566a",
+".u c #cacaca",
+".v c #9aeefe",
+".w c #7e7e7e",
+".x c #5e110d",
+".y c #022335",
+".z c #c4c4c4",
+".A c #5d5d5d",
+".B c #1e9aba",
+".C c #81ecfe",
+".D c #cb4e4b",
+".E c #021c2d",
+".F c #fe7e7e",
+".G c #451210",
+".H c #7ce2fd",
+".I c #74d6fa",
+".J c #b34645",
+".K c #525652",
+".L c #6eecfe",
+".M c #021622",
+".N c #be1e1e",
+".O c #3b3b3b",
+".P c #4ab2d6",
+".Q c #12d2fe",
+".R c #333333",
+".S c #270705",
+".T c #0e7292",
+".U c #02121c",
+".V c #9e6e86",
+".W c #0eb6fe",
+".X c #65eafe",
+".Y c #0a4662",
+".Z c #a31d1a",
+".0 c #220606",
+".1 c #3292be",
+".2 c #e6e6e6",
+".3 c #025d9d",
+".4 c #2ed6fe",
+".5 c #8c3d3e",
+".6 c #2496b4",
+".7 c #0e4c66",
+".8 c #d29ea2",
+".9 c #020e12",
+"#. c #fe928e",
+"## c #b0b0b0",
+"#a c #252525",
+"#b c #57ebfe",
+"#c c #2a2a2a",
+"#d c #1e1e1e",
+"#e c #424242",
+"#f c #63dbf9",
+"#g c #5bc9f1",
+"#h c #1e7e96",
+"#i c #025286",
+"#j c #7d1511",
+"#k c #0363a3",
+"#l c #a2768a",
+"#m c #2a2e2a",
+"#n c #5dcef7",
+"#o c #8e8e8e",
+"#p c #363a36",
+"#q c #4fe6fe",
+"#r c #2ea6ba",
+"#s c #0f74a8",
+"#t c #3db8ec",
+"#u c #464646",
+"#v c #1e0606",
+"#w c #249bd8",
+"#x c #2e322e",
+"#y c #1f6c79",
+"#z c #923636",
+"#A c #39dafe",
+"#B c #c66a76",
+"#C c #027ed6",
+"#D c #1a1a1a",
+"#E c #bdbdbd",
+"#F c #a23e3e",
+"#G c #024e82",
+"#H c #40aade",
+"#I c #06aefe",
+"#J c #0265ad",
+"#K c #7e2e2a",
+"#L c #e1e1e1",
+"#M c #fe9696",
+"#N c #48dafe",
+"#O c #020a10",
+"#P c #fe7677",
+"#Q c #43f3fe",
+"#R c #e66666",
+"#S c #25a9e0",
+"#T c #74b4ce",
+"#U c #1ac6fc",
+"#V c #521a1a",
+"#W c #929292",
+"#X c #c27682",
+"#Y c #666666",
+"#Z c #0272ba",
+"#0 c #42e6fe",
+"#1 c #1686a2",
+"#2 c #991e1a",
+"#3 c #728eae",
+"#4 c #30d0fc",
+"#5 c #0699f7",
+"#6 c #562a32",
+"#7 c #4a4a4a",
+"#8 c #023e64",
+"#9 c #7ff4fe",
+"a. c #16bafc",
+"a# c #0c6694",
+"aa c #161616",
+"ab c #fe9a9a",
+"ac c #ca2e2e",
+"ad c #6a6a6a",
+"ae c #47eefe",
+"af c #2cc4f3",
+"ag c #026ab0",
+"ah c #1a627c",
+"ai c #1a0606",
+"aj c #024a7c",
+"ak c #9d9d9d",
+"al c #3cdefe",
+"am c #8c262c",
+"an c #fa7a7e",
+"ao c #223252",
+"ap c #28b2e2",
+"aq c #168dc4",
+"ar c #5a1f1f",
+"as c #faaeaa",
+"at c #040608",
+"au c #fe9e9e",
+"av c #25b8e9",
+"aw c #a02e2c",
+"ax c #46bcf0",
+"ay c #fe7a7a",
+"az c #34aae4",
+"aA c #d2d2d2",
+"aB c #8e1c17",
+"aC c #1ca1d5",
+"aD c #ca625e",
+"aE c #0e0e0e",
+"aF c #fefefe",
+"aG c #6e6e6e",
+"aH c #d6bac6",
+"aI c #6af4fe",
+"aJ c #4ff3fe",
+"aK c #1e221e",
+"aL c #62eefe",
+"aM c #4e524e",
+"aN c #44eafe",
+"aO c #0a6294",
+"aP c #b6b6b6",
+"aQ c #0e6ea1",
+"aR c #1daddb",
+"aS c #3bd1f2",
+"aT c #1e96be",
+"aU c #0282de",
+"aV c #026ab9",
+"aW c #56e2fe",
+"aX c #a9a9a9",
+"aY c #be3e3a",
+"aZ c #0272c1",
+"a0 c #023e6c",
+"a1 c #f76565",
+"a2 c #1e86ba",
+"a3 c #5afafe",
+"a4 c #121212",
+"a5 c #e26e6e",
+"a6 c #0a8ec6",
+"a7 c #6e2222",
+"a8 c #f75d5c",
+"a9 c #360e0d",
+"b. c #feadad",
+"b# c #dd5a58",
+"ba c #023a62",
+"bb c #90f7fe",
+"bc c #76e2fe",
+"bd c #024274",
+"be c #14a6e8",
+"bf c #9eaabe",
+"bg c #858585",
+"bh c #f66e6c",
+"bi c #b61a16",
+"bj c #36e2fe",
+"bk c #035b93",
+"bl c #74f5fe",
+"bm c #56120e",
+"bn c #35c8f0",
+"bo c #120202",
+"bp c #831b17",
+"bq c #80fbfe",
+"br c #0b0a0b",
+"bs c #023256",
+"bt c #1382b9",
+"bu c #fe7171",
+"bv c #f0f0f0",
+"bw c #237a8a",
+"bx c #d8d8d8",
+"by c #068ce3",
+"bz c #4ae6fe",
+"bA c #1498dd",
+"bB c #362636",
+"bC c #2e2e46",
+"bD c #9e7e9a",
+"bE c #5aacc7",
+"bF c #24b2f8",
+"bG c #2cbce7",
+"bH c #2290cb",
+"bI c #9bfcfe",
+"bJ c #4aeafe",
+"bK c #89f3fe",
+"bL c #7e86a2",
+"bM c #50ebfe",
+"bN c #64f6fe",
+"bO c #026ebe",
+"bP c #024677",
+"bQ c #b53533",
+"bR c #123a5a",
+"bS c #02365b",
+"bT c #42e2fe",
+"bU c #3ad6f8",
+"bV c #0378c9",
+"bW c #3de7fe",
+"bX c #737373",
+"bY c #8aeefe",
+"bZ c #3ee2fe",
+"b0 c #cd3736",
+"b1 c #1876aa",
+"b2 c #02253f",
+"b3 c #f66a6b",
+"b4 c #2e9ed6",
+"b5 c #902e2a",
+"b6 c #0e79b7",
+"b7 c #626262",
+/* pixels */
+"..................................................................................................................................................................................................................................................",
+"..........................................ar.5.5.5.5#6.B#r#r#r#r#r#r#r#r#r#r#r#r#r#1..................................................................................at.7bwbwbwah#O..............................................................",
+"......................................a9.J.F.F.F.FaybD#QaNaNaNaNaNaNaNaNaNaNaNaNaN.Q...................................................................................yafaeaN#Qbn#O..............................................................",
+"....................................ar#Rayay#P#P#Pb3bEbWbTbTbZbZbZbZbZbZbZbZbZbZbW.a..................................................................................bSbU#0bTaNaR................................................................",
+"................................ai.Jayay#P#P#P#P#P#XaSbWbTbZ#q#b#b#b#b#b#b#b#b#b#9.W..................................................................................bkbZbTbTae.T................................................................",
+"..............................arb#ayay#P#P#P#P#Pbu#3bWbTbZ#qbYbc.H.H.Hbcbcbcbcbc#n#5.M................................................................................btbZbTbTae.Y................................................................",
+"..........................#v#F#Payay#Pa1ay#P#P#Pa8bnbWbTbZbl#t#JaVaVaVaVbObObObOaZby.M................................................................................be#0bTbT.#.U................................................................",
+"........................#Vb#ayayayb3bQ.hay#P#Pbu.V.#bTbTbT#q#4bGbGbGbGbGaCbOaVaVaZ#C.y.9.9.9.9.9.........U.9.9.9.9.9.U.U.U.U....at.U.9.9.9.9.9.9.9.9.9.9.9at.....Y#y#h#4#0bTbTal#y#y#y.Y..........................................................",
+"....................bo#zbuayay#P.h#2#2#P#P#P#Pb3bEbWbTbTbTbTbT#0#0#0#0bWa.#Saq#Saf#AalbZbZbZ#AbU.6.Ub2.6albZbZbZbWbWbWbWbWbW.6.EajavbUaSaSaSaSaSbUaSaSaSaSap.g#OavaJaN#0bTbTbZbWbWbW#Qav..........................................................",
+"...................Gb#ayayayb3b0#2#j.Day#P#P#P#BbnbWbTbTbTbTbTbTbTbZaN#UbG#0bZ#0#0#0#0bT#0bTbT#0a3#tapaN#0bTbTbJbTal#0bTbT#0aJaCbsaqaN#0#0#0bJ#AbU#A#0#0#0aNbTba#SbGaf#0bTbZ#0.LblblbqbE..........................................................",
+"................#zbuayay#Pa8ac#2#jbpb3ay#P#PbubL.#bTbZbZbZbjbjbjbjbzblbebUbTbTbZ#bbnaCaf#0bZbZ#b#b.balbTbTbZaI.XbHbtbUbTbTbTaJbA.oav#0bTbZ#b#gbO#ibtbTbTbTbJ#Ab2bdbab6#0bTbZ#q#gazaz#tah..........................................................",
+".............Gb#ayayayaya8ac.Zbp.xaYay#P#P#Pa1bnbWbTbZaLbY.C.C.C.CbYazbAbTbTbZbz.H.bbdb6bTbMaIbIbA.baNbTbZbTbYbAbP.dalbTbTbTbW.ba0#AbTbTbZ.XbH#ibs#S#0bTbZbMaR.ybabsaC#0bTbZaWbHbOaZbVb2..........................................................",
+"..........#Kbhay#Payabb3ac.ZaB.f.x#Ray#P#P#P#l.#bTbTbZ#9#waU.b.b.b#CbOav#0bTbZ#9#taV#8bd#sa2#H#waV#S#0bTbZbM.IbVa0btaNbTbT#0.4aVa#bTbTbTbZbc#Z.ebs#A#0bTbTaebtbsbaa0af#0bTbZ#fb6agaV#J.M..........................................................",
+"......a9b#ayay#Pauas.h.N.Zbp.f.xaw#P#P#P#PanbEbWbTbZbM.IbVbObOaVag#JbOalbTbZbTbba6.3babababd.dag#J#4#0bTbjbKbF#Jbdav#0bTbTaN#UbkaT#0bTbZbJ#g.dbS.daebTbTbTae#ibSba#ibUbTbTbW#f.d.3#k#G............................................................",
+".....Sb#.Fay#P.Fb..hb0b0bQawb5b5b#ay#P#Pbu.8aSbZbTbZ#9azaV#J#J#J#kbkaqaNbTbZbMbcbVbkba#8baa0#i.3b6bZbTbZbTbIbybkbkbUbTbTbZae#I.3bG#0bTbZblaTbPbSa2aNbTbT#0bjbababSaQalbTbZ#0.Paj.dbkbs............................................................",
+"....a7buay#P#P#P#P#Payayayayayayay#P#Pbu#.#TbWbTbZbzbKbV#kbkbkbk.d#ibn#0bTbZ.L#HaV#iba#8#8babPbPap#0bTbZ.X#NaU#GbtbZbTbZbTbMbyaOalbTbZbZ#9agba#8ap#0bTbZaebGbsbabSaq#0bTbZ#bb1bdaj#G.9............................................................",
+".....Dayayayayayayayayayayay#P#P#P#P#P#PasafbWbTbZaI#naVbk#i#i#i#GaQalbTbZbjbKby#Jajba#8#8ba#8bdalbTbTbZbMa.aVbdap#0bTbZ#b#NbVbtaNbTbZbM#f.dba#G#4#0bTbZbNaqbs#8baaR#0bTbTbz#Gba.e.e..............................................................",
+"..a9.c.ca1a1a1a1a1a1a1a1a1a1a1#P#P#P#P#.bfbjbTbTbZbKaT#k#ibdbdbda0#w#0bTbZ#b#nbO.3bPbSba#8#8bS#saNbTbTbTbT#4#SaSaNbTbTbZbqbF.3bG#0bTbZaL#tbS.E#sbWbTbZbZbq#G#O.9bS#4#0bTbT#4#sah.l.9..............................................................",
+"..bmaBbpbpbpbpbpbpbpbpbpbp#jbQbubububub.#TbjbZal#0.HbV.3aj#8a0#8bPaSbWbZbj#9aCaV.db2.9bs#8#8a0aCaebTbZbZbZbZbZbZbZbZbZ#qbbbA#kbUbZbZbj.LaT.9..aCaNbZbZbMbc.y....ajbj#0bZbZ#0aJ#Qa6................................................................",
+"...G.m.x.x.x.x.x.x.x.m.m.x.k.D#Mb.b.b.aHbnbNblblbIazaV.da0atat..bkbMaIblbl.CbV#k.e.....Ub2b2bS#JbnalbM#baLbNbNbNbNbNbl.vax#C#sbT#bblbq.Cag...Uav#0bJaLbI#t....#ObPaRbzbNaIbb#f#I#i................................................................",
+"...S.f.m.m.m.m.m.m.m.m.m.m.kam.JaDa5bh.t.3a2#H#tax.b#J#G.s......bd#s.1#Hax#SbO.3b2..........b2ba.daOaQb1bta2bHbHbHbHbH.b#CaVbda#b6bHb4#wbP...y#iaO#sbtb4b6.....Uba#Gbtb4az#waU#5.y................................................................",
+".....f.k.m.m.m.m.m.m.m.m.m.m.x.x.faBbiambababPbkaVbO.3bP.U....atbSbSba#i#kbO#k#iat...........s#8bababSbSbaa0bdbP#G#G.d#JbV#ibSbaa0#G.3bOb2...s#8bababPaga0.....E#8babaa0#G.3bVbV..................................................................",
+"....bm.k.m.m.m.m.m.m.m.m.m.m.m.m.kbp#2am#8ba#8#i#kag.dbS........bS#8#8bPbkag.3bS............b2#8ba#8ba#8#8#8a0bdbP#G#i#k#Zbs#8#8#8aj.3#k.U..bs#8#8babP#J.y.....s#8#8ba.e#GbkbO#G..................................................................",
+"....a9.k.k.m.k.m.k.m.m.m.m.m.m.m.m.k#jambR#8ba.e#i.3#G.y........bs#8ba#8aj.3#i.E.............E#8ba#8ba#8#8#8#8#8#8.ebP.d#k.E#8#8ba.e#i#G..atbS#8#8baa0#i.9....bS#8#8#8#8aj#ibkb2..................................................................",
+"....#v.k.k.m.k.m.k.m.m.m.k.m.m.m.m.m.fbpao#8#8ba.e#ibP#O.........o#8babaa0#G.eat.............E#8ba#8ba#8ba#8ba#8baba#8#G.e.E#8ba#8#8ajbS..#Obabababa#8ba......a0#8ba#8ba.eajbP.U..................................................................",
+".......0.0.0.0.0.0.0.0.0.0.0.G.k.m.m.m.fbC#8#8#8babdba..........b2#8#8ba#8bP.o...............E.e#8#8ba#8ba#8ba#8ba#8babPb2.E#8#8#8#8.e.E...9#8#8ba#8#8b2.....U.eba#8ba#8#8ba.U....................................................................",
+".............................S.m.k.k.m.kbB#8#8#8#8#8.E...........y#8#8#8#8#8.U..............at.s#8#8#8#8#8#8#8#8#8#8ba.satb2#8#8ba#8#8at...M#8#8#8#8#8.M....atbsa0#8#8a0.oat......................................................................",
+"...............................0.0#v#v.0br.U.M.U.M.Mat..........#O.M.U.M.U.M.....................M.U.U.U.U.U.U.U.U.M.9....#O.U.M.U.M.U....at.9.U.M.U.M........at.E.E.E.Eat........................................................................",
+"..................................................................................................................................................................................................................................................",
+"..........................aK#x#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#m#x#d",
+"..........................#p.KaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaM.K.R",
+"..........................#p.KaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaMaM.K.R",
+"..........................#d#m#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#c#m#d",
+"..................................................................................................................................................................................................................................................",
+".......................................................................................................OaMaMaMaMaM#7......................a4#E#W....................................bX.i...............................O#a........................",
+"......................................................................................................#EbxaAaF.zaAbv#e....................brbx.j....................................adaF..............................bx#o........................",
+"......................................................................................................ak....aF#a..ak#Y......................##ak....................................#maF..............................aGaM........................",
+"......................................................................................................#c....aF.R..#aaa......................##ak.....................................RaF....................................................at....",
+"............................................................................................................aF.R......ataX.q##.....RaPaFaX..###E#EaF.r..#u##.O##.i.R....#e##bv###a...RaF....#D#Ebvak#c..#DaG#Lbv.A.A#abg.p....bX#Lbx#a...K#LbvaM..",
+"............................................................................................................aF.R......##.p#DaXak...u.A#maX..###L#7bXaF..#daFaX#makbvaa..#EaK#d.j#E..#maF.....n.A#d#E##..aG.z#D.jaF.z..#L.r..bXaP#d.K.q.R.2#d#aak..",
+"............................................................................................................aF.R....aM#L.....waAbX##........##ak...RaFb7..bv.R....aF#Dad.2.....AaF#u#caF...r#E....#eaF.R.q.O..at.iaa...ubgat.ibr..#daF.OaFaK......",
+"............................................................................................................aF.R....aGaFaXaPbxaX#oak........aP.j...RaFad..bv.O..ataF#DbX#L.......i.K#caF..ak#E......aF.R.zaX..aK##.....ubgaEaFaPaP#Ebv#m.i.ibxbXbr",
+"............................................................................................................aF.R....aG#L........#W##........aP.j...RaFad..bv.O..ataF#DbX.2.......i.K#caF..ak#E......aF.R#uaFaGaA.O.....ubgaEaFat.........RbX.naF.O",
+"............................................................................................................aF#m....#7aFaM......bgbx........##ak...RaFad..bv.R..ataF#D.Abv.......n..#caF..bgaF#m..braF#dat##..br.......ubg..bv.j......aK.O.....i.O",
+"........................................................................................................aa.AaF.w......bx.nad.pb7#aaF##b7bX#ebx.j...RaFakataF#o#DataFaGaEbvaX...paP...waF#e#DaFaM..ak#W...AaF.wbgbgaE#d.2##...waFbXadbg#e##...OaP..",
+".........................................................................................................RaX#EaPaM....aK.z.u#YaE..bX.u.uaM.r#E###m...r###o#EaP#7..aX.j..#c.uakakaa..aX#Ebg..#u#Eak.j....#Ybx.u.n.i.n#e#E#Ea4...j.u#EaM#caAak##aE..",
+".........................................................................................................................................................................................................i#m.....A.q..............................",
+"........................................................................................................................................................................................................aF#a....#Ybx..............................",
+".........................................................................................................................................................................................................i.R....aAad..............................",
+".........................................................................................................................................................................................................O##bv.zb7................................",
+"..........................................................................................................................................................................................................at.OaE.................................."
+};