changeset 900:d985f0dcdeb0 trunk

[svn] - add a starting point for xmms-rootvis port. giacomo will need to finish this up, as my XLib skills are not enough at this time.
author nenolod
date Mon, 26 Mar 2007 01:19:26 -0700
parents 68508f8cdf25
children 08643d5994fe
files ChangeLog configure.ac mk/rules.mk.in src/rootvis/Makefile src/rootvis/config.h src/rootvis/config_backend.c src/rootvis/config_definition.c src/rootvis/config_frontend.c src/rootvis/config_frontend.h src/rootvis/config_frontend_widgets.c src/rootvis/getroot.c src/rootvis/rootvis.c src/rootvis/rootvis.h
diffstat 13 files changed, 2153 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Mar 25 09:26:39 2007 -0700
+++ b/ChangeLog	Mon Mar 26 01:19:26 2007 -0700
@@ -1,3 +1,10 @@
+2007-03-25 16:26:39 +0000  Giacomo Lozito <james@develia.org>
+  revision [1898]
+  - aosd: use XMapRaised in place of XMapWindow to ensure that the osd window is raised with some window managers
+  trunk/src/aosd/ghosd.c |    2 +-
+  1 file changed, 1 insertion(+), 1 deletion(-)
+
+
 2007-03-25 06:15:05 +0000  Yoshiki Yazawa <yaz@cc.rim.or.jp>
   revision [1896]
   - non-utf string had been used for file name in window title of File Info window. patch by M.H.
--- a/configure.ac	Sun Mar 25 09:26:39 2007 -0700
+++ b/configure.ac	Mon Mar 26 01:19:26 2007 -0700
@@ -387,6 +387,29 @@
     GENERAL_PLUGINS="$GENERAL_PLUGINS aosd"
 fi
 
+dnl *** Rootwindow vis plugin (imlib2 based)
+
+AC_ARG_ENABLE(rootvis,
+    [  --disable-rootvis       disable X11 RootWindow vis plugin (default=enabled)],
+    [enable_rootvis=$enableval],
+    [enable_rootvis="yes"]
+)
+
+if test "x$enable_rootvis" = "xyes"; then
+    have_rootvis="yes"
+    PKG_CHECK_MODULES(IMLIB2, [imlib2 >= 1.1.0],,
+        [have_rootvis="no"
+        AC_MSG_RESULT([*** imlib2 >= 1.1.0 is required for Audacious RootVis plugin ***])]
+    )
+else
+    AC_MSG_RESULT([*** RootVis plugin disabled per user request ***])
+    have_rootvis="no"
+fi
+
+if test "x$have_rootvis" = "xyes"; then
+    VISUALIZATION_PLUGINS="$VISUALIZATION_PLUGINS rootvis"
+fi
+
 dnl *** AdPlug requirement (libbinio)
 
 AC_ARG_ENABLE(adplug,
@@ -1234,6 +1257,7 @@
 echo "  Spectrum Analyzer:                      yes"
 echo "  Paranormal Visualization Library:       $have_paranormal"
 echo "  ProjectM (GL milkdrop):                 $have_projectm"
+echo "  RootVis plugin:                         $have_rootvis"
 echo
 echo "  Transport"
 echo "  ---------"
--- a/mk/rules.mk.in	Sun Mar 25 09:26:39 2007 -0700
+++ b/mk/rules.mk.in	Mon Mar 26 01:19:26 2007 -0700
@@ -351,3 +351,5 @@
 MMS_LIBS ?= @MMS_LIBS@
 MAD_CFLAGS ?= @MAD_CFLAGS@
 MAD_LIBS ?= @MAD_LIBS@
+IMLIB2_CFLAGS ?= @IMLIB2_CFLAGS@
+IMLIB2_LIBS ?= @IMLIB2_LIBS@
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/Makefile	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,22 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS = librootvis.so
+
+CFLAGS += -I. $(PICFLAGS) $(GTK_CFLAGS) $(GLIB_CFLAGS) $(PANGO_CFLAGS) $(IMLIB2_CFLAGS)
+
+LIBDIR = $(plugindir)/$(VISUALIZATION_PLUGIN_DIR)
+
+LIBADD += $(GTK_LIBS) $(GLIB_LIBS) $(PANGO_LIBS) $(IMLIB2_LIBS)
+
+SOURCES = \
+	config_backend.c \
+	config_definition.c \
+	config_frontend.c \
+	config_frontend_widgets.c \
+	getroot.c \
+	rootvis.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/config.h	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,57 @@
+enum valtype {
+	BOOLN,
+	INT,
+	FLOAT,
+	TEXT,
+	COLOR
+};
+
+void config_init(void);
+void config_revert(int);
+void config_save(int);
+
+void config_show(int);
+
+struct config_value_int {
+	int* var;
+	int def_value;
+	int range[2];
+};
+
+struct config_value_float {
+	float* var;
+	float def_value;
+	float range[2];
+};
+
+struct config_value_text {
+	char** var;
+	char* def_value;
+	int maxlength;
+};
+
+struct config_value_color {
+	unsigned char* var;
+	char* def_value;
+	void* frontend;
+};
+
+struct config_value {
+	enum valtype type;
+	char* name;
+	int affects;
+	union {
+		struct config_value_int vali;
+		struct config_value_float valf;
+		struct config_value_text valt;
+		struct config_value_color valc;
+	};
+};
+
+struct config_def {
+	int count;
+	struct config_value* def;
+};
+
+struct config_def Cmain;
+struct config_def Cchannel[2];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/config_backend.c	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,172 @@
+#include <string.h>
+#include <audacious/configdb.h>
+
+#include <rootvis.h>
+#include <config.h>
+
+extern void config_def_init(void);
+extern void config_frontend_init(void);
+
+void color_quad2arr(unsigned char* res, char* quad) {
+	if (sscanf(quad, "#%2hhx%2hhx%2hhx%2hhx", &res[0], &res[1], &res[2], &res[3]) != 4)
+		fprintf(stderr, "Color value %s could not be recognized as #rrggbbaa, ranging from #00000000 to #FFFFFFFF\n", quad);
+}
+
+char* color_arr2quad(unsigned char* src, char* quad) {
+	sprintf(quad, "#%2.2hhx%2.2hhx%2.2hhx%2.2hhx", src[0], src[1], src[2], src[3]);
+	return quad;
+}
+
+void cval_setdefault(struct config_value val)
+{
+	switch (val.type)
+	{
+		case BOOLN:
+		case INT:
+			*val.vali.var = val.vali.def_value;
+		break;
+		case FLOAT:
+			*val.valf.var = val.valf.def_value;
+		break;
+		case TEXT:
+			strcpy(*val.valt.var, val.valt.def_value);
+		break;
+		case COLOR:
+			color_quad2arr(val.valc.var, val.valc.def_value);
+		break;
+	}
+}
+
+void cval_writefile(struct config_value val, ConfigDb *fp, char* sect)
+{
+	switch (val.type)
+	{
+		case BOOLN:
+		case INT:
+			bmp_cfg_db_set_int(fp, sect, val.name, *val.vali.var);
+		break;
+		case FLOAT:
+			bmp_cfg_db_set_float(fp, sect, val.name, *val.valf.var);
+		break;
+		case TEXT:
+			bmp_cfg_db_set_string(fp, sect, val.name, *val.valt.var);
+		break;
+		case COLOR:
+		{
+			char colortmp[10];
+			bmp_cfg_db_set_string(fp, sect, val.name, color_arr2quad(val.valc.var, colortmp));
+		}
+		break;
+	}
+}
+
+void cval_readfile(struct config_value val, ConfigDb *fp, char* sect)
+{
+	switch (val.type)
+	{
+		case BOOLN:
+		case INT:
+			if (!(bmp_cfg_db_get_int(fp, sect, val.name, val.vali.var)))
+				cval_writefile(val, fp, sect);
+		break;
+		case FLOAT:
+			if (!(bmp_cfg_db_get_float(fp, sect, val.name, val.valf.var)))
+				cval_writefile(val, fp, sect);
+		break;
+		case TEXT:
+			if (!(bmp_cfg_db_get_string(fp, sect, val.name, val.valt.var)))
+				cval_writefile(val, fp, sect);
+		break;
+		case COLOR:
+		{
+			char* colortmp = NULL;
+			if (!(bmp_cfg_db_get_string(fp, sect, val.name, &colortmp)))
+				cval_writefile(val, fp, sect);
+			else	color_quad2arr(val.valc.var, colortmp);
+		}
+		break;
+	}
+}
+
+// this parses ~/.xmms/config
+// if a setting is not found, it is created to make it possible to edit the default settings
+// after the configuration dialogue is finished, this won't be necessary any more
+void config_read(int number) {
+	int i, j;
+	ConfigDb *fp;
+
+	fp = bmp_cfg_db_open();
+
+	print_status("Reading configuration");
+
+	if (number == 2)
+		for (i = 0; i < Cmain.count; ++i)
+		{
+			cval_setdefault(Cmain.def[i]);
+			cval_readfile(Cmain.def[i], fp, "rootvis");
+		}
+
+	for (j = 0; j < 2; ++j)
+		if ((number == j)||(number == 2))
+			for (i = 0; i < Cchannel[j].count; ++i)
+			{
+				cval_setdefault(Cchannel[j].def[i]);
+				cval_readfile(Cchannel[j].def[i], fp, (j == 0 ? "rootvis" : "rootvis2"));
+			}
+
+	bmp_cfg_db_close(fp);
+	print_status("Configuration finished");
+}
+
+void config_write(int number) {
+	int i, j;
+	ConfigDb *fp;
+
+	print_status("Writing configuration");
+	fp = bmp_cfg_db_open();
+
+	if (number == 2)
+		for (i = 0; i < Cmain.count; ++i)
+			cval_writefile(Cmain.def[i], fp, "rootvis");
+
+	for (j = 0; j < 2; ++j)
+		if ((number == j)||(number == 2))
+			for (i = 0; i < Cchannel[j].count; ++i)
+				cval_writefile(Cchannel[j].def[i], fp, (j == 0 ? "rootvis" : "rootvis2"));
+
+	bmp_cfg_db_close(fp);
+}
+
+void config_revert(int number) {
+	/* as the configs aren't saved in a thread save way, we have to lock while we read them */
+	threads_lock();
+	config_read(number);
+
+	// set the right change bits, according to wether we change channel 0, 1 or both (2)
+	if (number == 2) number = 15;
+	 else	number = 3 + number*9;
+	threads_unlock(number);
+}
+
+void config_save(int number) {
+	threads_lock();
+	config_write(number);
+	threads_unlock(0);
+}
+
+void config_init(void) {
+	static int initialized = 0;
+	if (initialized == 0) {
+		print_status("First initialization");
+
+		conf.geo[0].display = malloc(256);
+		conf.geo[1].display = malloc(256);
+
+		config_def_init();
+		config_frontend_init();
+
+		config_read(2);
+
+		initialized++;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/config_definition.c	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,152 @@
+#include <string.h>
+#include <rootvis.h>
+#include <config.h>
+
+inline void add_valb(struct config_def* conf, char* name, int affects, int* var, int def);
+inline void add_vali(struct config_def* conf, char* name, int affects, int* var, int def, int from, int to);
+inline void add_valf(struct config_def* conf, char* name, int affects, float* var, float def, float from, float to);
+inline void add_valt(struct config_def* conf, char* name, int affects, char** var, char* def, int maxlen);
+inline void add_valc(struct config_def* conf, char* name, int affects, unsigned char* var, char* def);
+
+void config_def_init(void)
+{
+	Cmain.count = 0;
+	add_valb(&Cmain, "debug", 0, &conf.debug, 0);
+	add_valb(&Cmain, "stereo", 32, &conf.stereo, 1);
+
+	Cchannel[0].count = 0;
+	add_valt(&Cchannel[0], "geometry_display", 32, &conf.geo[0].display, "", 255);
+	add_vali(&Cchannel[0], "geometry_posx", 1, &conf.geo[0].posx, 520, 0, 0);
+	add_vali(&Cchannel[0], "geometry_posy", 1, &conf.geo[0].posy, 1, 0, 0);
+	add_vali(&Cchannel[0], "geometry_orientation", 1, &conf.geo[0].orientation, 0, 0, 3);
+	add_vali(&Cchannel[0], "geometry_height", 1, &conf.geo[0].height, 50, 0, 0);
+	add_vali(&Cchannel[0], "geometry_space", 1, &conf.geo[0].space, 1, 0, 0);
+	add_vali(&Cchannel[0], "bar_width", 1, &conf.bar[0].width, 8, 0, 0);
+	add_vali(&Cchannel[0], "bar_falloff", 0, &conf.bar[0].falloff, 5, 1, 0);
+	add_vali(&Cchannel[0], "bar_shadow", 1, &conf.bar[0].shadow, 1, 0, 0);
+	add_valb(&Cchannel[0], "bar_bevel", 0, &conf.bar[0].bevel, 0);
+	add_valb(&Cchannel[0], "bar_gradient", 0, &conf.bar[0].gradient, 1);
+	add_valc(&Cchannel[0], "bar_color_1", 2, conf.bar[0].color[0], "#e6ff64FF");
+	add_valc(&Cchannel[0], "bar_color_2", 2, conf.bar[0].color[1], "#cdf62bFF");
+	add_valc(&Cchannel[0], "bar_color_3", 2, conf.bar[0].color[2], "#b8dd27FF");
+	add_valc(&Cchannel[0], "bar_color_4", 2, conf.bar[0].color[3], "#a3c422FF");
+	add_valc(&Cchannel[0], "bar_bevel_color", 2, conf.bar[0].bevel_color, "#00FF00FF");
+	add_valc(&Cchannel[0], "bar_shadow_color", 2, conf.bar[0].shadow_color, "#00000066");
+	add_valb(&Cchannel[0], "peak_enabled", 1, &conf.peak[0].enabled, 1);
+	add_vali(&Cchannel[0], "peak_falloff", 0, &conf.peak[0].falloff, 4, 1, 0);
+	add_vali(&Cchannel[0], "peak_step", 0, &conf.peak[0].step, 5, 0, 0);
+	add_valc(&Cchannel[0], "peak_color", 2, conf.peak[0].color, "#ffffffdd");
+	add_valb(&Cchannel[0], "peak_shadow", 0, &conf.peak[0].shadow, 0);
+	add_vali(&Cchannel[0], "data_cutoff", 1, &conf.data[0].cutoff, 180, 1, 255);
+	add_vali(&Cchannel[0], "data_div", 1, &conf.data[0].div, 4, 1, 255);
+	add_valf(&Cchannel[0], "data_linearity", 0, &conf.data[0].linearity, 0.33, 0.1, 0.9);
+	add_vali(&Cchannel[0], "data_fps", 0, &conf.data[0].fps, 30, 1, 100);
+
+	Cchannel[1].count = 0;
+	add_valt(&Cchannel[1], "geometry_display", 32, &conf.geo[1].display, "", 255);
+	add_vali(&Cchannel[1], "geometry_posx", 1, &conf.geo[1].posx, 520, 0, 0);
+	add_vali(&Cchannel[1], "geometry_posy", 1, &conf.geo[1].posy, 52, 0, 0);
+	add_vali(&Cchannel[1], "geometry_orientation", 1, &conf.geo[1].orientation, 1, 0, 3);
+	add_vali(&Cchannel[1], "geometry_height", 1, &conf.geo[1].height, 40, 0, 0);
+	add_vali(&Cchannel[1], "geometry_space", 1, &conf.geo[1].space, 2, 0, 0);
+	add_vali(&Cchannel[1], "bar_width", 1, &conf.bar[1].width, 8, 0, 0);
+	add_vali(&Cchannel[1], "bar_falloff", 0, &conf.bar[1].falloff, 5, 1, 0);
+	add_vali(&Cchannel[1], "bar_shadow", 1, &conf.bar[1].shadow, 0, 0, 0);
+	add_valb(&Cchannel[1], "bar_bevel", 0, &conf.bar[1].bevel, 0);
+	add_valb(&Cchannel[1], "bar_gradient", 0, &conf.bar[1].gradient, 1);
+	add_valc(&Cchannel[1], "bar_color_1", 2, conf.bar[1].color[0], "#e6ff6466");
+	add_valc(&Cchannel[1], "bar_color_2", 2, conf.bar[1].color[1], "#e6ff6455");
+	add_valc(&Cchannel[1], "bar_color_3", 2, conf.bar[1].color[2], "#e6ff6433");
+	add_valc(&Cchannel[1], "bar_color_4", 2, conf.bar[1].color[3], "#e6ff6422");
+	add_valc(&Cchannel[1], "bar_bevel_color", 2, conf.bar[1].bevel_color, "#00FF00FF");
+	add_valc(&Cchannel[1], "bar_shadow_color", 2, conf.bar[1].shadow_color, "#00000066");
+	add_valb(&Cchannel[1], "peak_enabled", 1, &conf.peak[1].enabled, 1);
+	add_vali(&Cchannel[1], "peak_falloff", 0, &conf.peak[1].falloff, 4, 1, 0);
+	add_vali(&Cchannel[1], "peak_step", 0, &conf.peak[1].step, 5, 0, 0);
+	add_valc(&Cchannel[1], "peak_color", 2, conf.peak[1].color, "#ffffff88");
+	add_valb(&Cchannel[1], "peak_shadow", 0, &conf.peak[1].shadow, 0);
+	add_vali(&Cchannel[1], "data_cutoff", 1, &conf.data[1].cutoff, 180, 1, 255);
+	add_vali(&Cchannel[1], "data_div", 1, &conf.data[1].div, 4, 1, 255);
+	add_valf(&Cchannel[1], "data_linearity", 0, &conf.data[1].linearity, 0.33, 0.1, 0.9);
+	add_vali(&Cchannel[1], "data_fps", 0, &conf.data[1].fps, 30, 1, 100);
+}
+
+inline void add_begin(struct config_def* conf, char* name, int affects)
+{
+	conf->def = realloc(conf->def, (conf->count+1)*sizeof(struct config_value));
+	conf->def[conf->count].name = (char*)malloc(strlen(name) + 1);
+	strcpy(conf->def[conf->count].name, name);
+	conf->def[conf->count].affects = affects;
+}
+
+inline void add_end(struct config_def* conf)
+{
+	conf->count++;
+}
+
+inline void add_valb(struct config_def* conf, char* name, int affects, int* var, int def)
+{
+	add_begin(conf, name, affects);
+
+	conf->def[conf->count].type = BOOLN;
+
+	conf->def[conf->count].vali.var = var;
+	conf->def[conf->count].vali.def_value = def;
+
+	add_end(conf);
+}
+
+inline void add_vali(struct config_def* conf, char* name, int affects, int* var, int def, int from, int to)
+{
+	add_begin(conf, name, affects);
+
+	conf->def[conf->count].type = INT;
+
+	conf->def[conf->count].vali.var = var;
+	conf->def[conf->count].vali.def_value = def;
+	conf->def[conf->count].vali.range[0] = from;
+	conf->def[conf->count].vali.range[1] = to;
+
+	add_end(conf);
+}
+
+inline void add_valf(struct config_def* conf, char* name, int affects, float* var, float def, float from, float to)
+{
+	add_begin(conf, name, affects);
+
+	conf->def[conf->count].type = FLOAT;
+
+	conf->def[conf->count].valf.var = var;
+	conf->def[conf->count].valf.def_value = def;
+	conf->def[conf->count].valf.range[0] = from;
+	conf->def[conf->count].valf.range[1] = to;
+
+	add_end(conf);
+}
+
+inline void add_valt(struct config_def* conf, char* name, int affects, char** var, char* def, int maxlen)
+{
+	add_begin(conf, name, affects);
+
+	conf->def[conf->count].type = TEXT;
+
+	conf->def[conf->count].valt.var = var;
+	conf->def[conf->count].valt.def_value = (char*)malloc(strlen(def) + 1);
+	strcpy(conf->def[conf->count].valt.def_value, def);
+	conf->def[conf->count].valt.maxlength = maxlen;
+
+	add_end(conf);
+}
+
+inline void add_valc(struct config_def* conf, char* name, int affects, unsigned char* var, char* def)
+{
+	add_begin(conf, name, affects);
+
+	conf->def[conf->count].type = COLOR;
+
+	conf->def[conf->count].valc.var = var;
+	conf->def[conf->count].valc.def_value = (char*)malloc(strlen(def) + 1);
+	strcpy(conf->def[conf->count].valc.def_value, def);
+
+	add_end(conf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/config_frontend.c	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,210 @@
+#include <stdarg.h>
+#include <string.h>
+
+#include <rootvis.h>
+
+#include <config_frontend.h>
+
+extern GtkWidget *frontend_create_channel(int channel);
+extern GtkWidget *frontend_create_main(void);
+extern void frontend_create_colorpicker(struct config_value*);
+
+void signal_revert(GtkWidget *togglebutton, gpointer data)
+{
+	config_revert(GPOINTER_TO_INT(data));
+}
+
+void signal_save(GtkWidget *togglebutton, gpointer data)
+{
+	config_save(GPOINTER_TO_INT(data));
+}
+
+void signal_show(GtkWidget *togglebutton, gpointer data)
+{
+	config_show(GPOINTER_TO_INT(data));
+}
+
+void signal_hide(GtkWidget *togglebutton, gpointer data)
+{
+	config_hide(GPOINTER_TO_INT(data));
+}
+
+void signal_toggle_colorselector(GtkWidget *w, struct config_value *cvar)
+{
+	struct rootvis_colorsel* colorsel = cvar->valc.frontend;
+	if (colorsel->window == NULL)
+		frontend_create_colorpicker(cvar);
+	gtk_widget_show(colorsel->window);
+}
+
+void signal_colorselector_ok(GtkWidget *w, struct config_value *cvar)
+{
+	struct rootvis_colorsel* colorsel = cvar->valc.frontend;
+	gtk_color_selection_get_color(GTK_COLOR_SELECTION(colorsel->color_picker), colorsel->color);
+	memcpy(colorsel->saved_color, colorsel->color, COLORSIZE*sizeof(gdouble));
+	frontend_update_color(cvar, 1);
+	gtk_widget_hide(colorsel->window);
+}
+
+void signal_colorselector_cancel(GtkWidget *w, struct config_value *cvar)
+{
+	struct rootvis_colorsel* colorsel = cvar->valc.frontend;
+	memcpy(colorsel->color, colorsel->saved_color, COLORSIZE*sizeof(gdouble));
+	frontend_update_color(cvar, 1);
+	gtk_widget_destroy(colorsel->window);
+	colorsel->window = NULL;
+}
+
+void signal_colorselector_update(GtkWidget *w, struct config_value *cvar)
+{
+	struct rootvis_colorsel* colorsel = cvar->valc.frontend;
+	gtk_color_selection_get_color(GTK_COLOR_SELECTION(colorsel->color_picker), colorsel->color);
+	frontend_update_color(cvar, 1);
+}
+
+void color_char2double(unsigned char source[4], gdouble dest[4])
+{
+	int i;
+	for (i = 0; i < COLORSIZE; ++i)
+	{
+		dest[i] = (double)source[i] / 255.0;
+	}
+}
+
+void color_double2char(double source[4], unsigned char dest[4])
+{
+	int i;
+	for (i = 0; i < COLORSIZE; ++i)
+	{
+		dest[i] = (int)(source[i] * 255.0);
+	}
+}
+
+// This was ripped from xmms-iris's config.c.
+void frontend_update_color(struct config_value *cvar, int system)
+{
+	struct rootvis_colorsel* colorsel = cvar->valc.frontend;
+	if (system > 0)
+	{
+		threads_lock();
+		color_double2char(colorsel->color, cvar->valc.var);
+		threads_unlock(2);
+	}
+
+	// following is among the dumbest shit I've ever seen. GtkPreview seems to suck a lot!
+	unsigned int i;
+	guchar color_buf[3*30];
+	char red, green, blue;
+	red = colorsel->color[RED]*0xff;
+	green = colorsel->color[GREEN]*0xff;
+	blue = colorsel->color[BLUE]*0xff;
+	for (i = 0; i < 30*3; i += 3) {
+		color_buf[i+RED] = (char) red;
+		color_buf[i+GREEN] = (char) green;
+		color_buf[i+BLUE] = (char) blue;
+	}
+	for (i = 0; i < 30; i++)
+		gtk_preview_draw_row(GTK_PREVIEW(colorsel->preview), color_buf, 0, i, 30);
+	gtk_widget_draw(colorsel->preview, NULL);
+}
+
+void frontend_set_color(struct config_value *cvar)
+{
+	struct rootvis_colorsel* colorsel = cvar->valc.frontend;
+	color_char2double(cvar->valc.var, colorsel->color);
+	memcpy(colorsel->saved_color, colorsel->color, COLORSIZE*sizeof(gdouble));
+}
+
+/* following functions catch signals from the gui widgets */
+
+int signal_window_close(GtkWidget *window, gpointer data)
+{
+	int number = 2;
+	if (window == widgets.window_channel[0]) number = 0;
+	if (window == widgets.window_channel[1]) number = 1;
+	config_hide(number);
+	return TRUE;
+}
+
+void signal_check_toggled(GtkWidget *togglebutton, gpointer data)
+{
+	printf("%s \n", (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton))) ? "TRUE" : "FALSE");
+}
+
+void signal_stereo_toggled(GtkWidget *togglebutton, gpointer data)
+{
+	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton))) {
+		gtk_label_set_text(widgets.stereo_status[0], "renders left channel");
+		gtk_label_set_text(widgets.stereo_status[1], "renders right channel");
+	} else {
+		gtk_label_set_text(widgets.stereo_status[0], "renders both channels");
+		gtk_label_set_text(widgets.stereo_status[1], "unused / inactive");
+	}
+}
+
+void signal_textentry_changed(GtkWidget *entry, gpointer data)
+{
+}
+
+void config_show_channel(int channel) {
+	if (widgets.window_channel[channel] == NULL) {
+		widgets.window_channel[channel] = frontend_create_channel(channel);
+	} else {
+		print_status("raising channel window");
+		gtk_widget_show(widgets.window_channel[channel]);
+	}
+}
+
+void config_show(int channel) {
+	if (channel == 2)
+	{
+		if (widgets.window_main == NULL) {
+			widgets.window_main = frontend_create_main();
+		} else {
+			print_status("raising windows");
+			gtk_widget_show(widgets.window_main);
+			if (widgets.window_channel[0] != NULL) {
+				gtk_widget_show(widgets.window_channel[0]);
+			}
+			if (widgets.window_channel[1] != NULL) {
+				gtk_widget_show(widgets.window_channel[1]);
+			}
+		}
+	} else config_show_channel(channel);
+}
+
+void config_hide(int number) {
+	/* hide or destroy? if destroy, pointers must be set to NULL */
+	if (number < 2) {
+		if (widgets.window_channel[number] != NULL)
+			gtk_widget_hide(widgets.window_channel[number]);
+		/* widgets.window_channel[number] = NULL; */
+	} else {
+		if (widgets.window_main != NULL)
+			gtk_widget_hide(widgets.window_main);
+		if (widgets.window_channel[0] != NULL)
+			gtk_widget_hide(widgets.window_channel[0]);
+		if (widgets.window_channel[1] != NULL)
+			gtk_widget_hide(widgets.window_channel[1]);
+		widgets.window_main = NULL;
+		widgets.window_channel[0] = NULL;
+		widgets.window_channel[1] = NULL;
+		// THIS SUCKS BEYOND REPAIR!
+	}
+}
+
+void config_set_widgets(int number)
+{
+	if (number < 2) {
+		// set per channel stuff
+	} else {
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widgets.stereo_check), ((conf.stereo>0) ? TRUE : FALSE));
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widgets.debug_check), ((conf.debug>0) ? TRUE : FALSE));
+	}
+}
+
+void config_frontend_init(void) {
+	widgets.window_main = NULL;
+	widgets.window_channel[0] = NULL;
+	widgets.window_channel[1] = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/config_frontend.h	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,62 @@
+#include <config.h>
+
+#define VBOX  	1
+#define HBOX  	2
+#define HBBOX 	3
+#define HBBOX2	4
+#define VBBOX 	5
+#define FRAME 	6
+
+#define ATTACH_TO_NOTEBOOK  1
+#define ATTACH_TO_CONTAINER 2
+#define ATTACH_TO_BOX       3
+
+#define ENTRY 1
+#define COMBO 2
+
+// this struct contains all we need to change a color:
+struct rootvis_colorsel
+{
+	GtkWidget *preview;           // the preview we need to update.
+	GtkWidget *button;            // the button that made it all
+			// ( to keep pressed ). FIXME
+	GtkWidget *label;             // the name left of the button.
+	GtkWidget *color_picker;      // the colorpicker that was launched.
+	GtkWidget *window;            // the window we launched.
+	gdouble   color[4];           // the color.
+	gdouble   saved_color[4];
+	char      *name;              // the name.
+				// (we'll use this to tune the colorpicker).
+	char      *complete_name;     // this is for the window's title.
+};
+
+// this is to contain pointers to various widgets...
+struct rootvis_frontend
+{
+	GtkWidget *window_main;
+	GtkWidget *window_channel[2];
+	GtkWidget *stereo_status[2];
+	GtkWidget *stereo_check;
+	GtkWidget *debug_check;
+} widgets;
+
+void config_hide(int);
+void config_set_widgets(int);
+
+int signal_window_close(GtkWidget *window, gpointer data);
+void signal_check_toggled(GtkWidget *togglebutton, gpointer data);
+void signal_stereo_toggled(GtkWidget *togglebutton, gpointer data);
+void signal_textentry_changed(GtkWidget *entry, gpointer data);
+void signal_toggle_colorselector(GtkWidget *button, struct config_value* cvar);
+void signal_colorselector_ok(GtkWidget *button, struct config_value* cvar);
+void signal_colorselector_cancel(GtkWidget *button, struct config_value* cvar);
+void signal_colorselector_update(GtkWidget *w, struct config_value* cvar);
+
+void signal_revert(GtkWidget *togglebutton, gpointer data);
+void signal_save(GtkWidget *togglebutton, gpointer data);
+void signal_show(GtkWidget *togglebutton, gpointer data);
+void signal_hide(GtkWidget *togglebutton, gpointer data);
+
+
+void frontend_update_color(struct config_value* cvar, int system);
+void frontend_set_color(struct config_value* cvar);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/config_frontend_widgets.c	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,486 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <config_frontend.h>
+
+extern void print_status(char msg[]);
+
+void frontend_set_signal(GtkWidget *widget, char* signal, void* func, int data)
+{
+	gtk_signal_connect(GTK_OBJECT(widget),	signal,	GTK_SIGNAL_FUNC(func),	GINT_TO_POINTER(data));
+}
+
+GtkWidget *frontend_create_window(int type, const char *name)
+{
+	GtkWidget *window;
+
+	print_status("creating window");
+	print_status(name);
+  	window = gtk_window_new(type);
+	gtk_signal_connect(GTK_OBJECT(window),
+				"delete-event",
+				GTK_SIGNAL_FUNC(signal_window_close),
+				NULL);
+
+	print_status("setting title");
+	gtk_window_set_title(window, name);
+	print_status("done");
+	gtk_widget_show(window);
+	return window;
+}
+
+GtkWidget *frontend_create_box(int box_type, GtkWidget *container, char *label,
+				int attach)
+{
+	GtkWidget *box;
+
+	print_status("creating box");
+	print_status(label);
+
+
+
+	switch (box_type) {
+	case VBOX:
+		box = gtk_vbox_new(FALSE, 5);
+		gtk_container_set_border_width(GTK_CONTAINER(box), 5);
+		break;
+
+	case HBOX:
+		box = gtk_hbox_new(FALSE, 5);
+		gtk_container_set_border_width(GTK_CONTAINER(box), 5);
+		break;
+	case HBBOX:
+		box = gtk_hbutton_box_new();
+		gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
+					  GTK_BUTTONBOX_END);
+		gtk_button_box_set_spacing(GTK_BUTTON_BOX(box), 5);
+		break;
+	case HBBOX2:
+		box = gtk_hbutton_box_new();
+		gtk_button_box_set_layout(GTK_BUTTON_BOX(box),
+					  GTK_BUTTONBOX_EDGE);
+		gtk_button_box_set_spacing(GTK_BUTTON_BOX(box), 4);
+		break;
+	case FRAME:
+		box = gtk_frame_new(label);
+		gtk_container_set_border_width(GTK_CONTAINER(box), 5);
+		break;
+	default:
+		print_status("error");
+		print_status("trying to create vbox");
+		box = gtk_vbox_new(FALSE, 5);
+		gtk_container_set_border_width(GTK_CONTAINER(box), 5);
+	}
+
+	print_status("attaching");
+	switch (attach) {
+	case ATTACH_TO_NOTEBOOK:
+		gtk_notebook_append_page(GTK_NOTEBOOK(container),
+					 box, gtk_label_new(label));
+		break;
+
+	case ATTACH_TO_CONTAINER:
+		gtk_container_add(GTK_CONTAINER(container), box);
+		break;
+	case ATTACH_TO_BOX:
+		gtk_box_pack_start(GTK_BOX(container), box, TRUE, TRUE, 0);
+		break;
+	default:
+		print_status("error");
+		print_status("trying to attach to container");
+		gtk_container_add(GTK_CONTAINER(container), box);
+	}
+	gtk_widget_show(box);
+	print_status("done");
+	return box;
+}
+
+GtkWidget *frontend_create_notebook(GtkWidget *box)
+{
+	GtkWidget *notebook;
+
+	print_status("creating notebook");
+	notebook = gtk_notebook_new();
+	gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, 0);
+	gtk_widget_show(notebook);
+	return notebook;
+}
+
+GtkWidget *frontend_create_button(GtkWidget *container, char *label)
+{
+	GtkWidget *button;
+
+	print_status("adding button");
+	print_status(label);
+
+	button = gtk_button_new_with_label(label);
+	gtk_widget_show(button);
+
+	GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+	gtk_box_pack_start(GTK_BOX(container), button, TRUE, TRUE, 0);
+
+	return button;
+}
+
+GtkWidget *frontend_create_check(GtkWidget *container, char *label)
+{
+	GtkWidget *check;
+
+	print_status("creating check");
+	print_status(label);
+
+	check = gtk_check_button_new_with_label(label);
+	gtk_widget_show(check);
+
+	gtk_container_add(GTK_CONTAINER(container), check);
+
+	print_status("done");
+	return check;
+}
+
+GtkWidget *frontend_create_label(GtkWidget *container, char *text)
+{
+	GtkWidget *label;
+
+	print_status("creating label");
+	print_status(text);
+
+	label = gtk_label_new(text);
+	gtk_label_set_justify(label, GTK_JUSTIFY_CENTER);
+	gtk_label_set_line_wrap(label, TRUE);
+	gtk_widget_show(label);
+
+	gtk_container_add(GTK_CONTAINER(container), label);
+
+	print_status("done");
+	return label;
+}
+
+/*
+GtkWidget *rootvis_create_frame_and_attach(char *name, GtkWidget *box)
+{
+	GtkWidget *frame;
+
+	frame = gtk_frame_new(name);
+	gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
+	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
+	gtk_widget_show(frame);
+	return frame;
+}
+*/
+
+GtkWidget *frontend_create_entry(int type, GtkWidget *container,
+					char *entry_changed,
+					char *label, ...)
+{
+	va_list ap;
+	char *list_element;
+	char *signal;
+
+	GtkWidget *entry;
+	GList *list = NULL;
+
+	print_status("creating entry");
+	print_status(label);
+
+	va_start(ap, label);
+	switch (type) {
+	case COMBO:
+		entry = gtk_combo_new();
+		while ((list_element = va_arg(ap, char *))) {
+			print_status("adding element to list");
+			print_status(list_element);
+			list = g_list_append(list, list_element);
+		}
+		print_status("attaching string list to combo");
+		gtk_combo_set_popdown_strings(GTK_COMBO(entry), list);
+		break;
+	case ENTRY:
+		entry = gtk_entry_new();
+		gtk_entry_set_max_length(GTK_ENTRY(entry), 6);
+		while ((signal = va_arg(ap, char *))) {
+			print_status("adding signal to entry");
+			print_status(signal);
+			gtk_signal_connect(GTK_OBJECT(entry),
+					   /* signal */
+					   signal,
+					   /* function */
+					   GTK_SIGNAL_FUNC(va_arg(ap,
+								  void *)),
+					   /* data */
+					   va_arg(ap, char *));
+		}
+		break;
+	default:
+		return NULL;
+	}
+	va_end(ap);
+
+	print_status("attaching entry to container");
+	gtk_container_add(GTK_CONTAINER(container), entry);
+	gtk_widget_show(entry);
+
+	print_status("done");
+
+	return entry;
+}
+
+void frontend_create_colorpicker(struct config_value *cvar)
+{
+	struct rootvis_colorsel* colorsel = cvar->valc.frontend;
+	GtkWidget *vbox;
+	GtkWidget *options_frame, *options_vbox;
+
+	static GtkWidget *bbox, *ok, *cancel;
+
+	print_status("pressing button ... ");
+	gtk_button_set_relief(GTK_BUTTON(colorsel->button), GTK_RELIEF_HALF);
+
+
+	print_status("casting window ...");
+
+	colorsel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+	gtk_window_set_title(GTK_WINDOW(colorsel->window), colorsel->complete_name);
+
+	gtk_container_set_border_width(GTK_CONTAINER(colorsel->window), 10);
+	gtk_window_set_policy(GTK_WINDOW(colorsel->window), FALSE, FALSE, FALSE);
+	gtk_window_set_position(GTK_WINDOW(colorsel->window),
+				GTK_WIN_POS_MOUSE);
+	gtk_signal_connect(GTK_OBJECT(colorsel->window), "destroy",
+			   GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+			   &(colorsel->window));
+
+	vbox = gtk_vbox_new(FALSE, 5);
+
+	printf("setting name ...");
+	options_frame = gtk_frame_new(colorsel->complete_name);
+	printf("done. \n");
+	gtk_container_set_border_width(GTK_CONTAINER(options_frame), 5);
+
+	options_vbox = gtk_vbox_new(FALSE, 5);
+	gtk_container_set_border_width(GTK_CONTAINER(options_vbox), 5);
+
+	colorsel->color_picker = gtk_color_selection_new();
+	gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(colorsel->color_picker), TRUE);
+	gtk_color_selection_set_color(GTK_COLOR_SELECTION(colorsel->color_picker), colorsel->color);
+	gtk_signal_connect(GTK_OBJECT(colorsel->color_picker), "color_changed", GTK_SIGNAL_FUNC(signal_colorselector_update), cvar);
+
+	gtk_box_pack_start(GTK_BOX(options_vbox), colorsel->color_picker,
+			   FALSE, FALSE, 0);
+        gtk_widget_show(colorsel->color_picker);
+	printf("raising the curtain \n");
+
+	gtk_container_add(GTK_CONTAINER(options_frame), options_vbox);
+	gtk_widget_show(options_vbox);
+
+	gtk_box_pack_start(GTK_BOX(vbox), options_frame, TRUE, TRUE, 0);
+	gtk_widget_show(options_frame);
+
+	bbox = gtk_hbutton_box_new();
+	gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+	gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+	gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+	ok = gtk_button_new_with_label("Ok");
+	gtk_signal_connect(GTK_OBJECT(ok), "clicked", GTK_SIGNAL_FUNC(signal_colorselector_ok), cvar);
+        GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
+	gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
+	gtk_widget_show(ok);
+
+
+	cancel = gtk_button_new_with_label("Cancel");
+	gtk_signal_connect(GTK_OBJECT(cancel), "clicked", GTK_SIGNAL_FUNC(signal_colorselector_cancel), cvar);
+	GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+	gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
+	gtk_widget_show(cancel);
+	gtk_widget_show(bbox);
+
+	gtk_container_add(GTK_CONTAINER(colorsel->window), vbox);
+	gtk_widget_show(vbox);
+	gtk_widget_grab_default(ok);
+}
+
+void frontend_create_color_button(struct config_value* cvar, GtkWidget *container, char *name,
+				  char *channel_name)
+{
+	struct rootvis_colorsel *color_struct;
+
+	print_status("Allocating memory for color struct");
+	color_struct = malloc(sizeof(struct rootvis_colorsel));
+	cvar->valc.frontend = color_struct;
+
+	frontend_set_color(cvar);
+	color_struct->window = NULL;
+	color_struct->name = name; //complete_name;
+
+	print_status("reallocating name");
+	color_struct->complete_name =
+	  malloc((sizeof(name) + sizeof(channel_name) + 10)*sizeof(char));
+	print_status("done");
+	sprintf(color_struct->complete_name, "%s - %s", channel_name, name);
+	print_status("done");
+
+
+	char* labeltext = (char*)malloc(strlen(name) + 2);
+	sprintf(labeltext, "%s:", name);
+	color_struct->label = gtk_label_new(labeltext);
+	gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(color_struct->label));
+
+	color_struct->button = gtk_button_new();
+	print_status("adding container ... ");
+	gtk_container_add(GTK_CONTAINER(container), GTK_WIDGET(color_struct->button));
+	print_status("done.\nraising ... ");
+
+	print_status("done.\nmaking preview ... ");
+
+	color_struct->preview = gtk_preview_new(GTK_PREVIEW_COLOR);
+	print_status("done.\nsetting size ... ");
+	gtk_preview_size(GTK_PREVIEW(color_struct->preview), 30, 28);
+	print_status("done.\nraising ... ");
+	print_status("done.\n");
+	gtk_container_add(GTK_CONTAINER(color_struct->button), color_struct->preview);
+
+	gtk_widget_set_usize(color_struct->button, 32, 26);
+
+	gtk_signal_connect(GTK_OBJECT(color_struct->button), "clicked",
+				GTK_SIGNAL_FUNC(signal_toggle_colorselector), cvar);
+
+	frontend_update_color(cvar, 0);
+	gtk_widget_show(GTK_WIDGET(color_struct->label));
+	gtk_widget_show(GTK_WIDGET(color_struct->button));
+	gtk_widget_show(color_struct->preview);
+}
+
+GtkWidget *frontend_create_channel(int channel)
+{
+	GtkWidget *window;
+	char name[12];
+
+	print_status("creating gtk window ... ");
+
+	sprintf(name, "Channel %d", channel+1);
+	print_status(name);
+
+	print_status("debug 2");
+
+	window = frontend_create_window(GTK_WINDOW_TOPLEVEL, &name);
+
+	print_status("done.");
+
+	{
+		GtkWidget *vbox_0, *notebook_1, *button_box_1,
+			*vbox_2[4], *frame_3[4], *vbox_3[1], *hbox_4[5],
+			*check_debug, *check_stereo,
+			*close_button, *revert_button;
+
+		vbox_0 = frontend_create_box(VBOX, window, "rootvis_config_vbox", ATTACH_TO_CONTAINER);
+		{
+
+			notebook_1 = frontend_create_notebook(vbox_0);
+			{
+			/*	vbox_2[0] = frontend_create_box(VBOX, notebook_1, "General", ATTACH_TO_NOTEBOOK);
+				{
+				}
+				vbox_2[1] = frontend_create_box(VBOX, notebook_1, "Geometry", ATTACH_TO_NOTEBOOK);
+				{
+				}
+				vbox_2[2] = frontend_create_box(VBOX, notebook_1, "Look & Feel", ATTACH_TO_NOTEBOOK);
+				{
+				}*/
+				vbox_2[3] = frontend_create_box(VBOX, notebook_1, "Colors", ATTACH_TO_NOTEBOOK);
+				{
+					frame_3[0] = frontend_create_box(FRAME, vbox_2[3], "Gradient", ATTACH_TO_BOX);
+					hbox_4[0] = frontend_create_box(HBOX, frame_3[0], "Bar", ATTACH_TO_CONTAINER);
+					{
+						frontend_create_color_button(&Cchannel[channel].def[11], hbox_4[0], "Begin", name);
+						frontend_create_color_button(&Cchannel[channel].def[12], hbox_4[0], "2/5", name);
+						frontend_create_color_button(&Cchannel[channel].def[13], hbox_4[0], "4/5", name);
+						frontend_create_color_button(&Cchannel[channel].def[14], hbox_4[0], "End", name);
+					}
+					frame_3[1] = frontend_create_box(FRAME, vbox_2[3], "Bevel, Peaks & Shadow", ATTACH_TO_BOX);
+					hbox_4[1] = frontend_create_box(HBOX, frame_3[1], "etc", ATTACH_TO_CONTAINER);
+					{
+						frontend_create_color_button(&Cchannel[channel].def[15], hbox_4[1], "Bevel", name);
+						frontend_create_color_button(&Cchannel[channel].def[20], hbox_4[1], "Peaks", name);
+						frontend_create_color_button(&Cchannel[channel].def[16], hbox_4[1], "Shadow", name);
+					}
+				}
+			}
+
+
+			button_box_1 = frontend_create_box(HBBOX2, vbox_0, "Button Box", ATTACH_TO_BOX);
+			{
+				revert_button = frontend_create_button(button_box_1, "Revert");
+				frontend_set_signal(revert_button, "clicked", signal_revert, channel);
+				close_button = frontend_create_button(button_box_1, "Close");
+				frontend_set_signal(close_button, "clicked", signal_hide, channel);
+			}
+		}
+	}
+	config_set_widgets(channel);
+	return window;
+}
+
+GtkWidget *frontend_create_main(void)
+{
+	GtkWidget *window, *channel_button[2],
+		  *button_box[2], *channels_frame, *main_frame, *vbox,
+		  *main_vbox, *channels_hbox, *channel_vbox[2],
+		  *save_button, *revert_button, *close_button;
+
+	window = frontend_create_window(GTK_WINDOW_TOPLEVEL, "Main");
+	{
+	 vbox = frontend_create_box(VBOX, window, "vbox", ATTACH_TO_CONTAINER);
+	 {
+	  main_frame = frontend_create_box(FRAME, vbox, "Global Settings", ATTACH_TO_BOX);
+	  {
+	   main_vbox = frontend_create_box(VBOX, main_frame, "main_hbox",
+					ATTACH_TO_CONTAINER);
+
+		widgets.stereo_check = frontend_create_check(main_vbox, "stereo support");
+		frontend_set_signal(widgets.stereo_check, "toggled", signal_stereo_toggled, 0);
+		widgets.debug_check = frontend_create_check(main_vbox, "debug messages (stdout)");
+		frontend_set_signal(widgets.debug_check, "toggled", signal_check_toggled, 0);
+
+	  }
+	  channels_frame = frontend_create_box(FRAME, vbox, "Channel-specific",
+					     ATTACH_TO_BOX);
+	  {
+	   button_box[0] = frontend_create_box(HBOX, channels_frame, "Main Button Box",
+					 ATTACH_TO_CONTAINER);
+	   {
+	    channel_vbox[0] = frontend_create_box(VBOX, button_box[0], "channel_vbox_0",
+					ATTACH_TO_CONTAINER);
+	    {
+		channel_button[0] = frontend_create_button(channel_vbox[0], "First Channel");
+		frontend_set_signal(channel_button[0], "clicked", signal_show, 0);
+		widgets.stereo_status[0] = frontend_create_label(channel_vbox[0], "renders both channels");
+	    }
+	    channel_vbox[1] = frontend_create_box(VBOX, button_box[0], "channel_vbox_0",
+					ATTACH_TO_CONTAINER);
+	    {
+
+		channel_button[1] = frontend_create_button(channel_vbox[1], "Second Channel");
+		frontend_set_signal(channel_button[1], "clicked", signal_show, 1);
+		widgets.stereo_status[1] = frontend_create_label(channel_vbox[1], "unused / inactive");
+	    }
+	   }
+	  }
+  	  button_box[1] = frontend_create_box(HBBOX, vbox, "Button Box", ATTACH_TO_BOX);
+	  {
+
+		revert_button = frontend_create_button(button_box[1], "Revert All");
+		frontend_set_signal(revert_button, "clicked", signal_revert, 2);
+		save_button = frontend_create_button(button_box[1], "Save Settings");
+		frontend_set_signal(save_button, "clicked", signal_save, 2);
+		close_button = frontend_create_button(button_box[1], "Close Windows");
+		frontend_set_signal(close_button, "clicked", signal_hide, 2);
+
+	  }
+	 }
+	}
+	config_set_widgets(2);
+	return window;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/getroot.c	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,400 @@
+/*  I've modified the following file and renamed it. My modifications were just
+ *  about getting rid of some toon.h dependants. If you're interested in the
+ *  original version of the file, you can find it either in the sources of
+ *  xpenguins or of xsnow.
+ *  http://xpenguins.seul.org/ is my actual source.
+ *
+ *  Johannes Jordan
+ */
+
+/* toon_root.c - finding the correct background window / virtual root
+ * Copyright (C) 1999-2001  Robin Hogan
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Since xpenguins version 2.1, the ToonGetRootWindow() function
+ * attempts to find the window IDs of
+ *
+ * 1) The background window that is behind the toplevel client
+ *    windows; this is the window that we draw the toons on.
+ *
+ * 2) The parent window of the toplevel client windows; this is used
+ *    by ToonLocateWindows() to build up a map of the space that the
+ *    toons can occupy.
+ *
+ * In simple (sensible?) window managers (e.g. blackbox, sawfish, fvwm
+ * and countless others), both of these are the root window. The other
+ * more complex scenarios that ToonGetRootWindow() attempts to cope
+ * with are:
+ *
+ * Some `virtual' window managers (e.g. amiwm, swm and tvtwm) that
+ * reparent all client windows to a desktop window that sits on top of
+ * the root window. This desktop window is easy to find - we just look
+ * for a property __SWM_VROOT in the immediate children of the root
+ * window that contains the window ID of this desktop window. The
+ * desktop plays both roles (1 and 2 above). This functionality was
+ * detected in xpenguins 1.x with the vroot.h header file.
+ *
+ * Enlightenment (0.16) can have a number of desktops with different
+ * backgrounds; client windows on these are reparented, except for
+ * Desktop 0 which is the root window. Therefore versions less than
+ * 2.1 of xpenguins worked on Desktop 0 but not on any others. To fix
+ * this we look for a root-window property _WIN_WORKSPACE which
+ * contains the numerical index of the currently active desktop. The
+ * active desktop is then simply the immediate child of the root
+ * window that has a property ENLIGHTENMENT_DESKTOP set to this value.
+ *
+ * KDE 2.0: Oh dear. The kdesktop is a program separate from the
+ * window manager that launches a window which sits behind all the
+ * other client windows and has all the icons on it. Thus the other
+ * client windows are still children of the root window, but we want
+ * to draw to the uppermost window of the kdesktop. This is difficult
+ * to find - it is the great-great-grandchild of the root window and
+ * in KDE 2.0 has nothing to identify it from its siblings other than
+ * its size. KDE 2.1+ usefully implements the __SWM_VROOT property in
+ * a child of the root window, but the client windows are still
+ * children of the root window. A problem is that the penguins erase
+ * the desktop icons when they walk which is a bit messy. The icons
+ * are not lost - they reappear when the desktop window gets an expose
+ * event (i.e. move some windows over where they were and back again).
+ *
+ * Nautilus (GNOME 1.4+): Creates a background window to draw icons
+ * on, but does not reparent the client windows. The toplevel window
+ * of the desktop is indicated by the root window property
+ * NAUTILUS_DESKTOP_WINDOW_ID, but then we must descend down the tree
+ * from this toplevel window looking for subwindows that are the same
+ * size as the screen. The bottom one is the one to draw to. Hopefully
+ * one day Nautilus will implement __SWM_VROOT in exactly the same way
+ * as KDE 2.1+.
+ *
+ * Other cases: CDE, the common desktop environment. This is a
+ * commercial product that has been packaged with Sun (and other)
+ * workstations. It typically implements four virtual desktops but
+ * provides NO properties at all for apps such as xpenguins to use to
+ * work out where to draw to. Seeing as Sun are moving over to GNOME,
+ * CDE use is on the decline so I don't have any current plans to try
+ * and get xpenguins to work with it.
+ *
+ * As a note to developers of window managers and big screen hoggers
+ * like kdesktop, please visit www.freedesktop.org and implement their
+ * Extended Window Manager Hints spec that help pagers and apps like
+ * xpenguins and xearth to find their way around. In particular,
+ * please use the _NET_CURRENT_DESKTOP and _NET_VIRTUAL_ROOTS
+ * properties if you reparent any windows (e.g. Enlightenment). Since
+ * no window managers that I know yet use these particular hints, I
+ * haven't yet added any code to parse them.  */
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+#include <string.h>
+
+/* Time to throw up. Here is a kludgey function that recursively calls
+ * itself (up to a limit) to find the window ID of the KDE desktop to
+ * draw on. It works with KDE 2.0, but since KDE 2.0 is less stable
+ * than Windows 95, I don't expect many people to remain using it now
+ * that 2.1 is available, which implements __SWM_VROOT and makes this
+ * function redundant. This is the hierarchy we're trying to traverse:
+ *
+ * -> The root window
+ * 0 -> window with name="KDE Desktop"
+ * 1   -> window with no name
+ * 2     -> window with name="KDE Desktop" & _NET_WM_WINDOW_TYPE_DESKTOP
+ * 3       -> window with no name and width >= width of screen
+ *
+ * The last window in the hierarchy is the one to draw to.  The
+ * numbers show the value of the `depth' argument.  */
+static
+Window
+__ToonGetKDEDesktop(Display *display, int screen, Window window,
+		    Atom atom, char *atomname, int depth)
+{
+  char *name = NULL;
+  unsigned char *wintype = NULL;
+  Window winreturn = 0;
+  unsigned long nitems, bytesafter;
+  Atom actual_type;
+  int actual_format;
+  Window rootReturn, parentReturn, *children;
+  unsigned int nChildren;
+  char go_deeper = 0;
+
+  if (XFetchName(display, window, &name)) {
+    if (strcasecmp(name, "KDE Desktop") == 0) {
+      /* Presumably either at depth 0 or 2 */
+      if (XGetWindowProperty(display, window, atom, 0, 1,
+			     False, XA_ATOM,
+			     &actual_type, &actual_format,
+			     &nitems, &bytesafter,
+			     &wintype) == Success
+	  && wintype) {
+	char *tmpatomname = XGetAtomName(display, *(Atom*)wintype);
+	if (tmpatomname) {
+	  if (strcmp(atomname, tmpatomname) == 0 && depth == 2) {
+	    /* OK, at depth 2 */
+	    go_deeper = 1;
+	  }
+	  XFree((char *) tmpatomname);
+	}
+      }
+      else if (depth < 2) {
+	go_deeper = 1;
+      }
+    }
+    else if (depth == 1) {
+      go_deeper = 1;
+    }
+    XFree((char *) name);
+  }
+  else if (depth == 1) {
+    go_deeper = 1;
+  }
+
+  /* If go_deeper is 1 then there is a possibility that the background
+   * window is a descendant of the current window; otherwise we're
+   * barking up the wrong tree. */
+  if (go_deeper && XQueryTree(display, window, &rootReturn,
+			      &parentReturn, &children,
+			      &nChildren)) {
+    int i;
+    for (i = 0; i < nChildren; ++i) {
+      /* children[i] is now at depth 3 */
+      if (depth == 2) {
+	XWindowAttributes attributes;
+	if (XGetWindowAttributes(display, children[i], &attributes)) {
+	  if (attributes.width >= DisplayWidth(display, screen)/2
+	      && attributes.height > 0) {
+	    /* Found it! */
+	    winreturn = children[i];
+	    break;
+	  }
+	}
+      }
+      else if ((winreturn = __ToonGetKDEDesktop(display, screen,
+						children[i],
+						atom, atomname,
+						depth+1))) {
+	break;
+      }
+    }
+    XFree((char *) children);
+  }
+
+  return winreturn;
+}
+
+/* Look for the Nautilus desktop window to draw to, given the toplevel
+ * window of the Nautilus desktop. Basically recursively calls itself
+ * looking for subwindows the same size as the root window. */
+static
+Window
+__ToonGetNautilusDesktop(Display *display, int screen, Window window,
+			 int depth)
+{
+  Window rootReturn, parentReturn, *children;
+  Window winreturn = window;
+  unsigned int nChildren;
+
+  if (depth > 5) {
+    return ((Window) 0);
+  }
+  else if (XQueryTree(display, window, &rootReturn, &parentReturn,
+		 &children, &nChildren)) {
+    int i;
+    for (i = 0; i < nChildren; ++i) {
+      XWindowAttributes attributes;
+      if (XGetWindowAttributes(display, children[i], &attributes)) {
+	if (attributes.width == DisplayWidth(display, screen)
+	    && attributes.height == DisplayHeight(display, screen)) {
+	  /* Found a possible desktop window */
+	  winreturn = __ToonGetNautilusDesktop(display, screen,
+					       children[i], depth+1);
+	}
+      }
+    }
+    XFree((char *) children);
+  }
+  return winreturn;
+}
+
+
+/*
+ * Returns the window ID of the `background' window on to which the
+ * toons should be drawn. Also returned (in clientparent) is the ID of
+ * the parent of all the client windows, since this may not be the
+ * same as the background window. If no recognised virtual window
+ * manager or desktop environment is found then the root window is
+ * returned in both cases. The string toon_message contains
+ * information about the window manager that was found.
+ */
+Window
+ToonGetRootWindow(Display *display, int screen, Window *clientparent)
+{
+  Window background = 0; /* The return value */
+  Window root = RootWindow(display, screen);
+  Window rootReturn, parentReturn, *children;
+  unsigned char *toplevel = NULL;
+  unsigned int nChildren;
+  unsigned long nitems, bytesafter;
+  Atom actual_type;
+  int actual_format;
+  unsigned char *workspace = NULL;
+  unsigned char *desktop = NULL;
+  Atom NAUTILUS_DESKTOP_WINDOW_ID = XInternAtom(display,
+			   "NAUTILUS_DESKTOP_WINDOW_ID",
+						False);
+
+  *clientparent = root;
+
+  if (XGetWindowProperty(display, root,
+			 NAUTILUS_DESKTOP_WINDOW_ID,
+			 0, 1, False, XA_WINDOW,
+			 &actual_type, &actual_format,
+			 &nitems, &bytesafter,
+			 &toplevel) == Success
+      && toplevel) {
+    /* Nautilus is running */
+    background = __ToonGetNautilusDesktop(display, screen,
+					*(Window*)toplevel, 0);
+    XFree(toplevel);
+  }
+
+  /* Next look for a virtual root or a KDE Desktop */
+  if (!background
+      && XQueryTree(display, root, &rootReturn, &parentReturn,
+		    &children, &nChildren)) {
+    int i;
+    Atom _NET_WM_WINDOW_TYPE = XInternAtom(display,
+			     "_NET_WM_WINDOW_TYPE",
+					   False);
+    Atom __SWM_VROOT = XInternAtom(display, "__SWM_VROOT", False);
+
+    for (i = 0; i < nChildren && !background; ++i) {
+      unsigned char *newroot = NULL;
+      if (XGetWindowProperty(display, children[i],
+			     __SWM_VROOT, 0, 1, False, XA_WINDOW,
+			     &actual_type, &actual_format,
+			     &nitems, &bytesafter,
+			     &newroot) == Success
+	  && newroot) {
+	/* Found a window with a __SWM_VROOT property that contains
+	 * the window ID of the virtual root. Now we must check
+	 * whether it is KDE (2.1+) or not. If it is KDE then it does
+	 * not reparent the clients. If the root window has the
+	 * _NET_SUPPORTED property but not the _NET_VIRTUAL_ROOTS
+	 * property then we assume it is KDE. */
+	Atom _NET_SUPPORTED = XInternAtom(display,
+					  "_NET_SUPPORTED",
+					  False);
+	unsigned char *tmpatom;
+	if (XGetWindowProperty(display, root,
+			       _NET_SUPPORTED, 0, 1, False,
+			       XA_ATOM, &actual_type, &actual_format,
+			       &nitems, &bytesafter,
+			       &tmpatom) == Success
+	    && tmpatom) {
+	  unsigned char *tmpwindow = NULL;
+	  Atom _NET_VIRTUAL_ROOTS = XInternAtom(display,
+						"_NET_VIRTUAL_ROOTS",
+						False);
+	  XFree(tmpatom);
+	  if (XGetWindowProperty(display, root,
+				 _NET_VIRTUAL_ROOTS, 0, 1, False,
+				 XA_WINDOW, &actual_type, &actual_format,
+				 &nitems, &bytesafter,
+				 &tmpwindow) != Success
+	      || !tmpwindow) {
+	    /* Must be KDE 2.1+ */
+	    background = *(Window*)newroot;
+	  }
+	  else if (tmpwindow) {
+	    XFree(tmpwindow);
+	  }
+	}
+
+	if (!background) {
+	  /* Not KDE: assume windows are reparented */
+	  background = *clientparent = *newroot;
+	}
+	XFree((char *) newroot);
+      }
+      else background = __ToonGetKDEDesktop(display, screen, children[i],
+						 _NET_WM_WINDOW_TYPE,
+						 "_NET_WM_WINDOW_TYPE_DESKTOP",
+						 0);
+    }
+    XFree((char *) children);
+  }
+
+  if (!background) {
+    /* Look for a _WIN_WORKSPACE property, used by Enlightenment */
+    Atom _WIN_WORKSPACE = XInternAtom(display, "_WIN_WORKSPACE", False);
+    if (XGetWindowProperty(display, root, _WIN_WORKSPACE,
+			   0, 1, False, XA_CARDINAL,
+			   &actual_type, &actual_format,
+			   &nitems, &bytesafter,
+			   &workspace) == Success
+	&& workspace) {
+      /* Found a _WIN_WORKSPACE property - this is the desktop to look for.
+       * For now assume that this is Enlightenment.
+       * We're looking for a child of the root window that has an
+       * ENLIGHTENMENT_DESKTOP atom with a value equal to the root window's
+       * _WIN_WORKSPACE atom. */
+      Atom ENLIGHTENMENT_DESKTOP = XInternAtom(display,
+					       "ENLIGHTENMENT_DESKTOP",
+					       False);
+      /* First check to see if the root window is the current desktop... */
+      if (XGetWindowProperty(display, root,
+			     ENLIGHTENMENT_DESKTOP, 0, 1,
+			     False, XA_CARDINAL,
+			     &actual_type, &actual_format,
+			     &nitems, &bytesafter,
+			     &desktop) == Success
+	  && desktop && *desktop == *workspace) {
+	/* The root window is the current Enlightenment desktop */
+	background = root;
+	XFree(desktop);
+      }
+      /* Now look at each immediate child window of root to see if it is
+       * the current desktop */
+      else if (XQueryTree(display, root, &rootReturn, &parentReturn,
+			  &children, &nChildren)) {
+	int i;
+	for (i = 0; i < nChildren; ++i) {
+	  if (XGetWindowProperty(display, children[i],
+				 ENLIGHTENMENT_DESKTOP, 0, 1,
+				 False, XA_CARDINAL,
+				 &actual_type, &actual_format,
+				 &nitems, &bytesafter,
+				 &desktop) == Success
+	      && desktop && *desktop == *workspace) {
+	    /* Found current Enlightenment desktop */
+	    background = *clientparent = children[i];
+	    XFree(desktop);
+	  }
+	}
+	XFree((char *) children);
+      }
+      XFree(workspace);
+    }
+  }
+  if (background) {
+    return background;
+  }
+  else {
+    return root;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/rootvis.c	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,456 @@
+#include <string.h>
+#include <math.h>
+#include <pthread.h>
+#include <time.h>
+
+#include "rootvis.h"
+// as imlib2 uses X definitions, it has to be included after the X includes, which are done in rootvis.h
+#include <Imlib2.h>
+#include "config.h"
+
+extern Window ToonGetRootWindow(Display*, int, Window*);
+
+// Forward declarations
+static void rootvis_init(void);
+static void rootvis_cleanup(void);
+static void rootvis_about(void);
+static void rootvis_configure(void);
+static void rootvis_playback_start(void);
+static void rootvis_playback_stop(void);
+static void rootvis_render_freq(gint16 freq_data[2][256]);
+
+// Callback functions
+VisPlugin rootvis_vtable = {
+	0, // Handle, filled in by xmms
+	0, // Filename, filled in by xmms
+
+	0,                     // Session ID
+	"Root Spectrum Analyzer 0.0.8",  // description
+
+	0, // # of PCM channels for render_pcm()
+	2, // # of freq channels wanted for render_freq()
+
+	rootvis_init,           // Called when plugin is enabled
+	rootvis_cleanup,        // Called when plugin is disabled
+	NULL,//rootvis_about,          // Show the about box
+	rootvis_configure,      // Show the configure box
+	0,                     // Called to disable plugin, filled in by xmms
+	rootvis_playback_start, // Called when playback starts
+	rootvis_playback_stop,  // Called when playback stops
+	0,                     // Render the PCM data, must return quickly
+	rootvis_render_freq     // Render the freq data, must return quickly
+};
+
+// XMMS entry point
+VisPlugin *get_vplugin_info(void) {
+	return &rootvis_vtable;
+}
+
+// X related
+struct rootvis_x {
+	int screen;
+	Display *display;
+	Window rootWin, Parent;
+	Pixmap rootBg;
+	GC gc;
+
+	Visual *vis;
+	Colormap cm;
+	Imlib_Image background;
+	Imlib_Image buffer;
+};
+
+// thread talk
+
+struct rootvis_threads {
+	gint16 freq_data[2][256];
+	pthread_t worker[2];
+	pthread_mutex_t mutex1;
+	enum {GO, STOP} control;
+	char dirty;
+	/*** dirty flaglist ***
+	  1: channel 1 geometry change
+	  2: channel 1 color change
+	  4: channel 2 geometry change
+	  8: channel 2 color change
+	 16: no data yet (don't do anything)
+	 32: switch mono/stereo
+	*/
+} threads;
+
+// For use in config_backend:
+
+void threads_lock(void) {
+	print_status("Locking");
+	pthread_mutex_lock(&threads.mutex1);
+}
+
+void threads_unlock(char dirty) {
+	print_status("Unlocking");
+	threads.dirty = threads.dirty & dirty;
+	pthread_mutex_unlock(&threads.mutex1);
+}
+
+// Some helper stuff
+
+void clean_data(void) {
+	pthread_mutex_lock(&threads.mutex1);
+	memset(threads.freq_data, 0, sizeof(gint16) * 2 * 256);
+	pthread_mutex_unlock(&threads.mutex1);
+}
+
+void print_status(char msg[]) {
+	if (conf.debug == 1) printf(">> rootvis >> %s\n", msg); // for debug purposes, but doesn't tell much anyway
+}
+
+void error_exit(char msg[]) {
+	printf("*** ERROR (rootvis): %s\n", msg);
+	rootvis_vtable.disable_plugin(&rootvis_vtable);
+}
+
+void initialize_X(struct rootvis_x* drw, char* display) {
+	print_status("Opening X Display");
+	drw->display = XOpenDisplay(display);
+	if (drw->display == NULL) {
+		fprintf(stderr, "cannot connect to X server %s\n",
+			getenv("DISPLAY") ? getenv("DISPLAY") : "(default)");
+		error_exit("Connecting to X server failed");
+		pthread_exit(NULL);
+	}
+	print_status("Getting screen and window");
+	drw->screen = DefaultScreen(drw->display);
+	drw->rootWin = ToonGetRootWindow(drw->display, drw->screen, &drw->Parent);
+
+	print_status("Initializing Imlib2");
+
+	drw->vis   = DefaultVisual(drw->display, drw->screen);
+	drw->cm    = DefaultColormap(drw->display, drw->screen);
+
+	imlib_context_set_display(drw->display);
+	imlib_context_set_visual(drw->vis);
+	imlib_context_set_colormap(drw->cm);
+
+	imlib_context_set_dither(0);
+	imlib_context_set_blend(1);
+}
+
+void draw_init(struct rootvis_x* drw, unsigned short damage_coords[4])
+{
+	Atom tmp_rootmapid, tmp_type;
+    int tmp_format;
+    unsigned long tmp_length, tmp_after;
+    unsigned char *data = NULL;
+
+	if ((tmp_rootmapid = XInternAtom(drw->display, "_XROOTPMAP_ID", True)) != None)
+	{
+		int ret = XGetWindowProperty(drw->display, drw->rootWin, tmp_rootmapid, 0L, 1L, False, AnyPropertyType,
+										&tmp_type, &tmp_format, &tmp_length, &tmp_after,&data);
+		if ((ret == Success)&&(tmp_type == XA_PIXMAP)&&((drw->rootBg = *((Pixmap *)data)) != None)) {
+			pthread_mutex_lock(&threads.mutex1);
+			imlib_context_set_drawable(drw->rootBg);
+			drw->background = imlib_create_image_from_drawable(0, damage_coords[0], damage_coords[1], damage_coords[2], damage_coords[3], 1);
+			pthread_mutex_unlock(&threads.mutex1);
+		}
+		if (drw->background == NULL)
+			error_exit("Initial image could not be created");
+	}
+}
+
+void draw_close(struct rootvis_x* drw, unsigned short damage_coords[4]) {
+	pthread_mutex_lock(&threads.mutex1);
+	imlib_context_set_image(drw->background);
+	imlib_render_image_on_drawable(damage_coords[0], damage_coords[1]);
+	XClearArea(drw->display, drw->rootWin, damage_coords[0], damage_coords[1], damage_coords[2], damage_coords[3], True);
+	imlib_free_image();
+	pthread_mutex_unlock(&threads.mutex1);
+}
+
+void draw_start(struct rootvis_x* drw, unsigned short damage_coords[4]) {
+	imlib_context_set_image(drw->background);
+	drw->buffer = imlib_clone_image();
+	imlib_context_set_image(drw->buffer);
+}
+
+void draw_end(struct rootvis_x* drw, unsigned short damage_coords[4]) {
+	imlib_context_set_drawable(drw->rootWin);
+	imlib_render_image_on_drawable(damage_coords[0], damage_coords[1]);
+	imlib_free_image();
+}
+
+void draw_bar(struct rootvis_x* drw, int t, int i, unsigned short level, unsigned short oldlevel, unsigned short peak, unsigned short oldpeak) {
+
+	/* to make following cleaner, we work with redundant helper variables
+	   this also avoids some calculations */
+	register int a, b, c, d;
+	float angle;
+	Imlib_Color_Range range = imlib_create_color_range();
+
+	if (conf.geo[t].orientation < 2) {
+		a = i*(conf.bar[t].width + conf.bar[t].shadow + conf.geo[t].space);
+		c = conf.bar[t].width;
+		b = d = 0;
+	} else {
+		b = (conf.data[t].cutoff/conf.data[t].div - i - 1)
+			*(conf.bar[t].width + conf.bar[t].shadow + conf.geo[t].space);
+		d = conf.bar[t].width;
+		a = c = 0;
+	}
+
+	if (conf.geo[t].orientation == 0) {	b = conf.geo[t].height - level; d = level; }
+	else if (conf.geo[t].orientation == 1) { b = 0; d = level; }
+	else if (conf.geo[t].orientation == 2) { a = 0; c = level; }
+	else	{ a = conf.geo[t].height - level; c = level; }
+
+	if (conf.bar[t].shadow > 0) {
+		imlib_context_set_color(conf.bar[t].shadow_color[0], conf.bar[t].shadow_color[1],
+								conf.bar[t].shadow_color[2], conf.bar[t].shadow_color[3]);
+		if (conf.bar[t].gradient)
+			imlib_image_fill_rectangle(a + conf.bar[t].shadow, b + conf.bar[t].shadow, c, d);
+		else if (conf.bar[t].bevel)
+			imlib_image_draw_rectangle(a + conf.bar[t].shadow, b + conf.bar[t].shadow, c, d);
+
+		if (conf.peak[t].shadow > 0)
+		{
+			int aa = a, bb = b, cc = c, dd = d;
+			if (conf.geo[t].orientation == 0) {	bb = conf.geo[t].height - peak; dd = 1; }
+			else if (conf.geo[t].orientation == 1) { bb = peak - 1; dd = 1; }
+			else if (conf.geo[t].orientation == 2) { aa = peak - 1; cc = 1; }
+			else	{ aa = conf.geo[t].height - peak; cc = 1; }
+			imlib_image_fill_rectangle(aa + conf.bar[t].shadow, bb + conf.bar[t].shadow, cc, dd);
+		}
+	}
+
+	if (conf.bar[t].gradient)
+	{
+		switch (conf.geo[t].orientation) {
+			case 0:	angle = 0.0; break;
+			case 1:	angle = 180.0; break;
+			case 2:	angle = 90.0; break;
+			case 3:	default:
+					angle = -90.0;
+		}
+
+		imlib_context_set_color_range(range);
+		imlib_context_set_color(conf.bar[t].color[3][0], conf.bar[t].color[3][1], conf.bar[t].color[3][2], conf.bar[t].color[3][3]);
+		imlib_add_color_to_color_range(0);
+		imlib_context_set_color(conf.bar[t].color[2][0], conf.bar[t].color[2][1], conf.bar[t].color[2][2], conf.bar[t].color[2][3]);
+		imlib_add_color_to_color_range(level * 2 / 5);
+		imlib_context_set_color(conf.bar[t].color[1][0], conf.bar[t].color[1][1], conf.bar[t].color[1][2], conf.bar[t].color[1][3]);
+		imlib_add_color_to_color_range(level * 4 / 5);
+		imlib_context_set_color(conf.bar[t].color[0][0], conf.bar[t].color[0][1], conf.bar[t].color[0][2], conf.bar[t].color[0][3]);
+		imlib_add_color_to_color_range(level);
+		imlib_image_fill_color_range_rectangle(a, b, c, d, angle);
+		imlib_free_color_range();
+	}
+
+	if (conf.bar[t].bevel)
+	{
+		imlib_context_set_color(conf.bar[t].bevel_color[0], conf.bar[t].bevel_color[1],
+								conf.bar[t].bevel_color[2], conf.bar[t].bevel_color[3]);
+		imlib_image_draw_rectangle(a, b, c, d);
+	}
+
+	if (peak > 0) {
+		if (conf.geo[t].orientation == 0) {	b = conf.geo[t].height - peak; d = 1; }
+		else if (conf.geo[t].orientation == 1) { b = peak - 1; d = 1; }
+		else if (conf.geo[t].orientation == 2) { a = peak - 1; c = 1; }
+		else	{ a = conf.geo[t].height - peak; c = 1; }
+		imlib_context_set_color(conf.peak[t].color[0], conf.peak[t].color[1], conf.peak[t].color[2], conf.peak[t].color[3]);
+		imlib_image_fill_rectangle(a, b, c, d);
+	}
+}
+
+// Our worker thread
+
+void* worker_func(void* threadnump) {
+	struct rootvis_x draw;
+	gint16 freq_data[256];
+	double scale = 0.0, x00 = 0.0, y00 = 0.0;
+	unsigned int threadnum, i, j, level;
+	unsigned short damage_coords[4];
+	unsigned short *level1 = NULL, *level2 = NULL, *levelsw, *peak1 = NULL, *peak2 = NULL, *peakstep;
+	int barcount = 0;
+
+	if (threadnump == NULL) threadnum = 0; else threadnum = 1;
+
+	print_status("Memory allocations");
+	level1 = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out
+	level2 = (unsigned short*)malloc(256*sizeof(short));
+	peak1 = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out
+	peak2 = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out for disabled peaks
+	peakstep = (unsigned short*)calloc(256, sizeof(short)); // need to be zeroed out
+	if ((level1 == NULL)||(level2 == NULL)||(peak1 == NULL)||(peak2 == NULL)||(peakstep == NULL)) {
+		error_exit("Allocation of memory failed");
+		pthread_exit(NULL);
+	}
+	print_status("Allocations done");
+
+	draw.display = NULL;
+
+	while (threads.control != STOP) {
+
+		{
+			//print_status("start sleep");
+			struct timespec sleeptime;
+			sleeptime.tv_sec = 0;
+			sleeptime.tv_nsec = 999999999 / conf.data[threadnum].fps;
+			while (nanosleep(&sleeptime, &sleeptime) == -1) {}; //print_status("INTR");
+			//print_status("end sleep");
+		}
+
+		/* we will unset our own dirty flags after receiving them */
+		pthread_mutex_lock(&threads.mutex1);
+		memcpy(&freq_data, &threads.freq_data[threadnum], sizeof(gint16)*256);
+		i = threads.dirty;
+		if ((i & 16) == 0) threads.dirty = i & (~(3 + threadnum*9));
+		pthread_mutex_unlock(&threads.mutex1);
+
+		if ((i & 16) == 0) { // we've gotten data
+			if (draw.display == NULL)	initialize_X(&draw, conf.geo[threadnum].display);
+			else if (i & (1 + threadnum*3)) draw_close(&draw, damage_coords);
+
+			if (i & (1 + threadnum*3)) {	// geometry has changed
+				damage_coords[0] = conf.geo[threadnum].posx;
+				damage_coords[1] = conf.geo[threadnum].posy;
+				if (conf.geo[threadnum].orientation < 2) {
+					damage_coords[2] = conf.data[threadnum].cutoff/conf.data[threadnum].div
+						*(conf.bar[threadnum].width + conf.bar[threadnum].shadow + conf.geo[threadnum].space);
+					damage_coords[3] = conf.geo[threadnum].height + conf.bar[threadnum].shadow;
+				} else {
+					damage_coords[2] = conf.geo[threadnum].height + conf.bar[threadnum].shadow;
+					damage_coords[3] = conf.data[threadnum].cutoff/conf.data[threadnum].div
+						*(conf.bar[threadnum].width + conf.bar[threadnum].shadow + conf.geo[threadnum].space);
+				}
+				print_status("Geometry recalculations");
+				scale = conf.geo[threadnum].height /
+					(log((1 - conf.data[threadnum].linearity) / conf.data[threadnum].linearity) * 4);
+				x00 = conf.data[threadnum].linearity*conf.data[threadnum].linearity*32768.0 /
+					(2*conf.data[threadnum].linearity - 1);
+				y00 = -log(-x00) * scale;
+				barcount = conf.data[threadnum].cutoff/conf.data[threadnum].div;
+				memset(level1, 0, 256*sizeof(short));
+				memset(peak1, 0, 256*sizeof(short));
+				memset(peak2, 0, 256*sizeof(short));
+
+				draw_init(&draw, damage_coords);
+			}
+			/*if (i & (2 + threadnum*6)) {	// colors have changed
+			}*/
+
+			/* instead of copying the old level array to the second array,
+				we just tell the first is now the second one */
+			levelsw = level1;
+			level1 = level2;
+			level2 = levelsw;
+			levelsw = peak1;
+			peak1 = peak2;
+			peak2 = levelsw;
+
+			for (i = 0; i < barcount; i++) {
+				level = 0;
+				for (j = i*conf.data[threadnum].div; j < (i+1)*conf.data[threadnum].div; j++)
+					if (level < freq_data[j])
+						level = freq_data[j];
+				level = level * (i*conf.data[threadnum].div + 1);
+				level = floor(abs(log(level - x00)*scale + y00));
+				if (level < conf.geo[threadnum].height) {
+					if ((level2[i] > conf.bar[threadnum].falloff)&&(level < level2[i] - conf.bar[threadnum].falloff))
+						level1[i] = level2[i] - conf.bar[threadnum].falloff;
+					else	level1[i] = level;
+				} else level1[i] = conf.geo[threadnum].height;
+				if (conf.peak[threadnum].enabled) {
+					if (level1[i] > peak2[i] - conf.peak[threadnum].falloff) {
+						peak1[i] = level1[i];
+						peakstep[i] = 0;
+					} else if (peakstep[i] == conf.peak[threadnum].step)
+						if (peak2[i] > conf.peak[threadnum].falloff)
+							peak1[i] = peak2[i] - conf.peak[threadnum].falloff;
+						else peak1[i] = 0;
+					else {
+						peak1[i] = peak2[i];
+						peakstep[i]++;
+					}
+				}
+			}
+
+			pthread_mutex_lock(&threads.mutex1);
+			draw_start(&draw, damage_coords);
+			for (i = 0; i < barcount; i++)
+				draw_bar(&draw, threadnum, i, level1[i], level2[i], peak1[i], peak2[i]);
+			draw_end(&draw, damage_coords);
+			pthread_mutex_unlock(&threads.mutex1);
+		}
+	}
+	print_status("Worker thread: Exiting");
+	if (draw.display != NULL) {
+		draw_close(&draw, damage_coords);
+		XCloseDisplay(draw.display);
+	}
+	free(level1);	free(level2);	free(peak1);	free(peak2);	free(peakstep);
+	return NULL;
+}
+
+
+// da xmms functions
+
+static void rootvis_init(void) {
+	int rc1;
+	print_status("Initializing");
+	pthread_mutex_init(&threads.mutex1, NULL);
+	threads.control = GO;
+	clean_data();
+	config_init();
+	threads.dirty = 31;	// this means simply everything has changed and there was no data
+	if ((rc1 = pthread_create(&threads.worker[0], NULL, worker_func, NULL))) {
+		fprintf(stderr, "Thread creation failed: %d\n", rc1);
+		error_exit("Thread creation failed");
+	}
+	if ((conf.stereo)&&(rc1 = pthread_create(&threads.worker[1], NULL, worker_func, &rc1))) {
+		fprintf(stderr, "Thread creation failed: %d\n", rc1);
+		error_exit("Thread creation failed");
+	}
+}
+
+static void rootvis_cleanup(void) {
+	print_status("Cleanup... ");
+	threads.control = STOP;
+	pthread_join(threads.worker[0], NULL);
+	if (conf.stereo)	pthread_join(threads.worker[1], NULL);
+	print_status("Clean Exit");
+}
+
+static void rootvis_about(void)
+{
+	print_status("About");
+}
+
+static void rootvis_configure(void)
+{
+	print_status("Configuration trigger");
+	config_init();
+	config_show(2);
+}
+
+static void rootvis_playback_start(void)
+{
+	print_status("Playback starting");
+}
+
+static void rootvis_playback_stop(void)
+{
+	clean_data();
+}
+
+static void rootvis_render_freq(gint16 freq_data[2][256]) {
+	int channel, bucket;
+	pthread_mutex_lock(&threads.mutex1);
+	threads.dirty = threads.dirty & (~(16)); // unset no data yet flag
+	for (channel = 0; channel < 2; channel++) {
+	 for (bucket = 0; bucket < 256; bucket++) {
+		if (conf.stereo) threads.freq_data[channel][bucket] = freq_data[channel][bucket];
+		else if (channel == 0) threads.freq_data[0][bucket] = freq_data[channel][bucket] / 2;
+			else	threads.freq_data[0][bucket] += freq_data[channel][bucket] / 2;
+	 }
+	}
+	pthread_mutex_unlock(&threads.mutex1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rootvis/rootvis.h	Mon Mar 26 01:19:26 2007 -0700
@@ -0,0 +1,103 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+#include <audacious/plugin.h>
+#include <audacious/util.h>
+
+
+/* following values are used if there is no user configuration */
+#define DEFAULT_stereo 0 // therefore we don't initialize the second channel with different settings
+
+#define DEFAULT_geometry_posx 520
+#define DEFAULT_geometry_posy 1
+#define DEFAULT_geometry_orientation 0 // 0 = bottom->up, 1 = top->down, 2 = left->right, 3 = right->left
+#define DEFAULT_geometry_height 50 // maximum height/amplitude of a bar
+#define DEFAULT_geometry_space 1 // space between bars
+#define DEFAULT_bar_width 8 // width of a bar
+// set the following to 0 to disable shadows
+#define DEFAULT_bar_shadow 1 // offset of shadow in pixels
+// set the following to HEIGHT to disable falloff
+#define DEFAULT_bar_falloff 5 // how many pixels the bars should falloff every frame
+#define DEFAULT_bar_color_1 "#a3c422FF"
+#define DEFAULT_bar_color_2 "#b8dd27FF"
+#define DEFAULT_bar_color_3 "#cdf62bFF"
+#define DEFAULT_bar_color_4 "#e6ff64FF"
+#define DEFAULT_bar_shadow_color "#00285088"
+
+// set the following to 0 to disable peaks
+#define DEFAULT_peak_enabled	1
+#define DEFAULT_peak_falloff	4 // how many pixels the peaks should falloff every frame
+#define DEFAULT_peak_step	5 // how many frames should the peak resist the falloff
+#define DEFAULT_peak_color "#ffffffdd"
+
+// we're cutting off high frequencies by only showing 0 to CUTOFF
+#define DEFAULT_cutoff 180 // frequencies are represented by 256 values
+#define DEFAULT_div 4 // we have CUTOFF sources, every bar represents DIV sources
+
+/* Linearity of the amplitude scale (0.5 for linear, keep in [0.1, 0.9]) */
+#define DEFAULT_linearity 0.33
+#define DEFAULT_fps	30 // how many frames per second should be drawn
+
+// print out debug messages
+#define DEFAULT_debug 0
+
+// this is for color[] indexing
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define ALPHA 3
+#define COLORSIZE 4
+
+
+void print_status(char msg[]);
+void error_exit(char msg[]);
+
+void threads_lock(void);
+void threads_unlock(char);
+
+struct rootvis_geometry {
+	char* display;
+	int posx;
+	int posy;
+	int orientation;
+	int height;
+	int space;
+};
+
+struct rootvis_bar {
+	int width;
+	int shadow;
+	int falloff;
+	int bevel;
+	int gradient;
+	unsigned char color[4][4];
+	unsigned char bevel_color[4];
+	unsigned char shadow_color[4];
+};
+
+struct rootvis_peak {
+	int enabled;
+	int falloff;
+	int step;
+	int shadow;
+	unsigned char color[4];
+};
+
+struct rootvis_data {
+	int cutoff;
+	int div;
+	int fps;
+	float linearity;
+};
+
+struct rootvis_config {
+	int stereo;
+	struct rootvis_geometry geo[2];
+	struct rootvis_bar bar[2];
+	struct rootvis_peak peak[2];
+	struct rootvis_data data[2];
+	int debug;
+} conf;