changeset 408:290588854a9d trunk

[svn] - rovascope -- a variant of the paranormal visualization engine that is uses random data instead of presets.
author nenolod
date Sat, 06 Jan 2007 01:57:34 -0800
parents 3c0cb7e84e0d
children 2bf7e2965eec
files ChangeLog configure.ac src/rovascope/Makefile src/rovascope/TODO src/rovascope/actuators.c src/rovascope/actuators.h src/rovascope/beatdetect.c src/rovascope/builtins.c src/rovascope/cmaps.c src/rovascope/containers.c src/rovascope/containers.h src/rovascope/drawing.c src/rovascope/drawing.h src/rovascope/freq.c src/rovascope/general.c src/rovascope/libcalc/Makefile src/rovascope/libcalc/calc.h src/rovascope/libcalc/dict.c src/rovascope/libcalc/dict.h src/rovascope/libcalc/execute.c src/rovascope/libcalc/execute.h src/rovascope/libcalc/function.c src/rovascope/libcalc/function.h src/rovascope/libcalc/parser.c src/rovascope/libcalc/parser.h src/rovascope/libcalc/parser.y src/rovascope/libcalc/storage.c src/rovascope/libcalc/storage.h src/rovascope/misc.c src/rovascope/paranormal.c src/rovascope/paranormal.h src/rovascope/plugin.c src/rovascope/pn_utils.h src/rovascope/wave.c src/rovascope/xform.c
diffstat 35 files changed, 6018 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Jan 05 19:31:47 2007 -0800
+++ b/ChangeLog	Sat Jan 06 01:57:34 2007 -0800
@@ -1,3 +1,11 @@
+2007-01-06 03:31:47 +0000  William Pitcock <nenolod@nenolod.net>
+  revision [892]
+  - update to Objective-Make II
+  
+  trunk/mk/objective.mk |   41 +++++++++++++++++++++++++++++++++--------
+  1 file changed, 33 insertions(+), 8 deletions(-)
+
+
 2007-01-05 12:54:33 +0000  Giacomo Lozito <james@develia.org>
   revision [890]
   - removed gdk_threads_enter/leave from song_change.c, it's not needed here and brings troubles too
--- a/configure.ac	Fri Jan 05 19:31:47 2007 -0800
+++ b/configure.ac	Sat Jan 06 01:57:34 2007 -0800
@@ -966,7 +966,7 @@
 ])
 
 if test "$have_paranormal" = "yes"; then
-	VISUALIZATION_PLUGINS="$VISUALIZATION_PLUGINS paranormal"
+	VISUALIZATION_PLUGINS="$VISUALIZATION_PLUGINS paranormal rovascope"
 fi
 
 if test "$have_xspf" = "yes"; then
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/Makefile	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,32 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS = librovascope$(SHARED_SUFFIX)
+
+SUBDIRS = libcalc
+
+LIBDIR = $(plugindir)/$(VISUALIZATION_PLUGIN_DIR)
+
+LIBADD = $(GTK_LIBS) $(XML_LIBS) $(SDL_LIBS) libcalc/libcalc.a
+
+SOURCES =			\
+	actuators.c		\
+	actuatorbin.c		\
+	beatdetect.c		\
+	builtins.c		\
+	cmaps.c			\
+	containers.c		\
+	drawing.c		\
+	freq.c			\
+	general.c		\
+	misc.c			\
+	paranormal.c		\
+	plugin.c		\
+	wave.c			\
+	xform.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(ARCH_DEFINES) $(XML_CPPFLAGS) $(SDL_CFLAGS) -I../../intl -I../..
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/TODO	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,13 @@
+More immediate points:
+
+- blend surface
+
+- container_stepped
+  executes <step> amount of instructions.. step increases or decreases 
+  on beat
+
+- container_repeat
+  executes the child instructions <step> amount of times.. step 
+  increases or decreases on beat
+
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/actuators.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,179 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "actuators.h"
+//#include "containers.h"
+
+/* FIXME: container options override containees - fix this? */
+/* FIXME: add actuator groups (by a group name string) */
+
+/* FIXME: add support for copying containers' children (optionally) */
+struct pn_actuator *
+copy_actuator (const struct pn_actuator *a)
+{
+  struct pn_actuator *actuator;
+  int i;    
+
+  actuator = g_new (struct pn_actuator, 1);
+
+  actuator->desc = a->desc;
+
+  /* Make an options table */
+  if (actuator->desc->option_descs)
+    {
+      /* count the options */
+      for (i=0; actuator->desc->option_descs[i].name; i++);
+
+      actuator->options = g_new (struct pn_actuator_option, i);
+      for (i=0; actuator->desc->option_descs[i].name; i++)
+	{
+	  actuator->options[i].desc = &actuator->desc->option_descs[i];
+
+	  /* copy the default options */
+	  switch (actuator->desc->option_descs[i].type)
+	    {
+	    case OPT_TYPE_INT:
+	    case OPT_TYPE_COLOR_INDEX:
+	    case OPT_TYPE_FLOAT:
+	    case OPT_TYPE_COLOR:
+	    case OPT_TYPE_BOOLEAN:
+	      memcpy (&actuator->options[i].val,
+		      &a->options[i].val,
+		      sizeof (union actuator_option_val));
+	      break;
+	    case OPT_TYPE_STRING:
+              actuator->options[i].val.sval = g_strdup(a->options[i].val.sval);
+              break;
+	    default:
+	      break;
+	    }
+	}
+
+      /* the NULL option */
+      actuator->options[i].desc = NULL;
+    }
+  else
+    actuator->options = NULL;
+
+  if (actuator->desc->init)
+    actuator->desc->init (&actuator->data);
+
+  return actuator;
+}
+
+struct pn_actuator_desc *
+get_actuator_desc (const char *name)
+{
+  int i;
+
+  for (i=0; builtin_table[i]; i++)
+    if (! g_strcasecmp (name, builtin_table[i]->name) || ! g_strcasecmp(name, builtin_table[i]->dispname))
+      break;
+
+  /* actuator not found */
+  if (! builtin_table[i])
+    return NULL;
+
+  return builtin_table[i];
+}
+
+struct pn_actuator *
+create_actuator (const char *name)
+{
+  int i;
+  struct pn_actuator_desc *desc;
+  struct pn_actuator *actuator;
+
+  /* find the actuatoreration */
+  desc = get_actuator_desc (name);
+
+  if (! desc)
+    return NULL;
+
+  actuator = g_new (struct pn_actuator, 1);
+  actuator->desc = desc;
+
+  /* Make an options table */
+  if (actuator->desc->option_descs)
+    {
+      /* count the options */
+      for (i=0; actuator->desc->option_descs[i].name != NULL; i++);
+
+      actuator->options = g_new0 (struct pn_actuator_option, i + 1);
+      for (i=0; actuator->desc->option_descs[i].name != NULL; i++)
+	{
+	  actuator->options[i].desc = &actuator->desc->option_descs[i];
+
+	  /* copy the default options */
+	  switch (actuator->desc->option_descs[i].type)
+	    {
+	    case OPT_TYPE_INT:
+	    case OPT_TYPE_COLOR_INDEX:
+	    case OPT_TYPE_FLOAT:
+	    case OPT_TYPE_COLOR:
+	    case OPT_TYPE_BOOLEAN:
+	      memcpy (&actuator->options[i].val,
+		      &actuator->desc->option_descs[i].default_val,
+		      sizeof (union actuator_option_val));
+	      break;
+	    case OPT_TYPE_STRING:
+	      /* NOTE: It's not realloc'ed so don't free it */
+	      actuator->options[i].val.sval =
+		actuator->desc->option_descs[i].default_val.sval;
+	      break;
+	    }
+	}
+
+      /* the NULL option */
+      actuator->options[i].desc = NULL;
+    }
+  else
+    actuator->options = NULL;
+
+  if (actuator->desc->init)
+    actuator->desc->init (&actuator->data);
+
+  return actuator;
+}
+
+void
+destroy_actuator (struct pn_actuator *actuator)
+{
+  int i;
+
+  if (actuator->desc->cleanup)
+    actuator->desc->cleanup (actuator->data);
+
+  /* find any option val's that need to be freed */
+  if (actuator->options)
+    for (i=0; actuator->options[i].desc; i++)
+      switch (actuator->options[i].desc->type)
+	{
+	case OPT_TYPE_INT:
+	case OPT_TYPE_FLOAT:
+	case OPT_TYPE_COLOR:
+	case OPT_TYPE_COLOR_INDEX:
+	case OPT_TYPE_BOOLEAN:
+	  break;
+	case OPT_TYPE_STRING:
+	  if (actuator->options[i].val.sval
+	      != actuator->options[i].desc->default_val.sval)
+	    g_free ((char *)actuator->options[i].val.sval);
+	}
+
+  g_free (actuator->options);
+  g_free (actuator);
+}
+
+void
+exec_actuator (struct pn_actuator *actuator)
+{
+  g_assert (actuator);
+  g_assert (actuator->desc);
+  g_assert (actuator->desc->exec);
+  actuator->desc->exec (actuator->options, actuator->data);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/actuators.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,99 @@
+/* FIXME: rename actuators to pn_actuators */
+/* FIXME: add a color type to the OPT_TYPE's */
+
+#ifndef _ACTUATORS_H
+#define _ACTUATORS_H
+
+#include <glib.h>
+#include <SDL.h>
+
+/* Helper macros for actuator functions */
+#define PN_ACTUATOR_INIT_FUNC(f) ((void (*) (gpointer *data))f)
+#define PN_ACTUATOR_CLEANUP_FUNC(f) ((void (*) (gpointer data))f)
+#define PN_ACTUATOR_EXEC_FUNC(f) \
+((void (*) (const struct pn_actuator_option *opts, gpointer data))f)
+
+struct pn_color
+{
+  guchar r, g, b;
+  guchar spluzz; /* gotta look like an SDL_Color */
+};
+
+
+union actuator_option_val
+{
+  int ival;
+  float fval;
+  const char *sval;
+  struct pn_color cval;
+  gboolean bval;
+};
+
+/* A actuator's option description */
+struct pn_actuator_option_desc
+{
+  const char *name;
+  const char *doc; /* option documentation */
+  enum
+  {
+    OPT_TYPE_INT = 0,
+    OPT_TYPE_FLOAT = 1,
+    OPT_TYPE_STRING = 2,
+    OPT_TYPE_COLOR = 3,
+    OPT_TYPE_COLOR_INDEX = 4, /* uses ival */
+    OPT_TYPE_BOOLEAN = 5
+  } type;
+  union actuator_option_val default_val;
+};
+
+/* The actual option instance */
+struct pn_actuator_option
+{
+  const struct pn_actuator_option_desc *desc;
+  union actuator_option_val val;
+};
+
+/* An operation's description */
+struct pn_actuator_desc
+{
+  const char *name;	/* e.g. "container_simple" */
+  const char *dispname;	/* e.g. "Simple Container" */
+  const char *doc; /* documentation txt */
+  const enum
+  {
+    ACTUATOR_FLAG_CONTAINER = 1<<0
+  } flags;
+
+  /* A null terminating (ie a actuator_option_desc == {0,...,0})
+     array - OPTIONAL (optional fields can be NULL) */
+  const struct pn_actuator_option_desc *option_descs;
+
+  /* Init function - data points to the actuator.data - OPTIONAL */
+  void (*init) (gpointer *data);
+  
+  /* Cleanup actuatortion - OPTIONAL */
+  void (*cleanup) (gpointer data);
+
+  /* Execute actuatortion - REQUIRED (duh!) */
+  void (*exec) (const struct pn_actuator_option *opts, gpointer data);
+};
+
+/* An actual operation instance */
+struct pn_actuator
+{
+  const struct pn_actuator_desc *desc;
+  struct pn_actuator_option *options;
+  gpointer data;
+};
+
+/* The array containing all operations (see builtins.c) */
+extern struct pn_actuator_desc *builtin_table[];
+
+/* functions for actuators */
+struct pn_actuator_desc *get_actuator_desc (const char *name);
+struct pn_actuator *copy_actuator (const struct pn_actuator *a);
+struct pn_actuator *create_actuator (const char *name);
+void destroy_actuator (struct pn_actuator *actuator);
+void exec_actuator (struct pn_actuator *actuator);
+
+#endif /* _ACTUATORS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/beatdetect.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,30 @@
+#include "paranormal.h"
+
+/*
+ * This algorithm is by Janusz Gregorcyzk, the implementation is
+ * mine, however.
+ *
+ *   -- nenolod
+ */
+int
+pn_is_new_beat(void)
+{
+  gint i;
+  gint total = 0;
+  gboolean ret = FALSE;
+  static gint previous;
+
+  for (i = 1; i < 512; i++)
+    {
+       total += abs (pn_sound_data->pcm_data[0][i] -
+		     pn_sound_data->pcm_data[0][i - 1]);
+    }
+
+  total /= 512;
+
+  ret = (total > (2 * previous));
+
+  previous = total;
+
+  return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/builtins.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,102 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "actuators.h"
+
+#define DECLARE_ACTUATOR(a) extern struct pn_actuator_desc builtin_##a;
+
+/* **************** containers **************** */
+
+DECLARE_ACTUATOR (container_simple);
+DECLARE_ACTUATOR (container_once);
+DECLARE_ACTUATOR (container_cycle);
+DECLARE_ACTUATOR (container_onbeat);
+
+/* **************** cmaps **************** */
+
+DECLARE_ACTUATOR (cmap_bwgradient);
+DECLARE_ACTUATOR (cmap_gradient);
+DECLARE_ACTUATOR (cmap_dynamic);
+
+/* **************** freq **************** */
+DECLARE_ACTUATOR (freq_dots);
+DECLARE_ACTUATOR (freq_drops);
+
+/* **************** general **************** */
+DECLARE_ACTUATOR (general_fade);
+DECLARE_ACTUATOR (general_blur);
+DECLARE_ACTUATOR (general_mosaic);
+DECLARE_ACTUATOR (general_clear);
+DECLARE_ACTUATOR (general_noop);
+DECLARE_ACTUATOR (general_invert);
+DECLARE_ACTUATOR (general_replace);
+DECLARE_ACTUATOR (general_swap);
+DECLARE_ACTUATOR (general_copy);
+DECLARE_ACTUATOR (general_flip);
+
+/* **************** misc **************** */
+DECLARE_ACTUATOR (misc_floater);
+
+/* **************** wave **************** */
+DECLARE_ACTUATOR (wave_horizontal);
+DECLARE_ACTUATOR (wave_vertical);
+DECLARE_ACTUATOR (wave_normalize);
+DECLARE_ACTUATOR (wave_smooth);
+DECLARE_ACTUATOR (wave_radial);
+DECLARE_ACTUATOR (wave_scope);
+
+/* **************** xform **************** */
+DECLARE_ACTUATOR (xform_spin);
+DECLARE_ACTUATOR (xform_ripple);
+DECLARE_ACTUATOR (xform_bump_spin);
+DECLARE_ACTUATOR (xform_halfrender);
+DECLARE_ACTUATOR (xform_movement);
+DECLARE_ACTUATOR (xform_dynmovement);
+
+/* **************** builtin_table **************** */
+struct pn_actuator_desc *builtin_table[] =
+{
+  /* **************** containers **************** */
+  &builtin_container_simple,
+  &builtin_container_once,
+  &builtin_container_cycle,
+  &builtin_container_onbeat,
+
+  /* **************** cmaps **************** */
+  &builtin_cmap_bwgradient,
+  &builtin_cmap_gradient,
+  &builtin_cmap_dynamic,
+  /* **************** freq **************** */
+  &builtin_freq_dots,
+  &builtin_freq_drops,
+  /* **************** general **************** */
+  &builtin_general_fade,
+  &builtin_general_blur,
+  &builtin_general_mosaic,
+  &builtin_general_clear,
+  &builtin_general_noop,
+  &builtin_general_invert,
+  &builtin_general_replace,
+  &builtin_general_swap,
+  &builtin_general_copy,
+  &builtin_general_flip,
+  /* **************** misc **************** */
+  &builtin_misc_floater,
+  /* **************** wave **************** */
+  &builtin_wave_horizontal,
+  &builtin_wave_vertical,
+  &builtin_wave_normalize,
+  &builtin_wave_smooth,
+  &builtin_wave_radial,
+  &builtin_wave_scope,
+  /* **************** xform **************** */
+  &builtin_xform_spin,
+  &builtin_xform_ripple,
+  &builtin_xform_bump_spin,
+  &builtin_xform_halfrender,
+  &builtin_xform_movement,
+  &builtin_xform_dynmovement,
+  /* **************** the end! **************** */
+  NULL
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/cmaps.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,185 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+
+#include "libcalc/calc.h"
+
+#define STD_CMAP_OPTS { "low_index", "The lowest index of the \
+color map that should be altered", OPT_TYPE_COLOR_INDEX, { ival: 0 } },\
+{ "high_index", "The highest index of the color map that should be \
+altered", OPT_TYPE_COLOR_INDEX, { ival: 255 } }
+
+static struct pn_color black = {0, 0, 0};
+static struct pn_color white = {255, 255, 255};
+
+/* **************** cmap generation funcs **************** */
+static void
+cmap_gen_gradient (int step, const struct pn_color *a,
+		   const struct pn_color *b,
+		   struct pn_color *c)
+{
+  c->r = a->r + step * ((((float)b->r) - ((float)a->r)) / 256.0);
+  c->g = a->g + step * ((((float)b->g) - ((float)a->g)) / 256.0);
+  c->b = a->b + step * ((((float)b->b) - ((float)a->b)) / 256.0);
+}
+
+/* **************** cmap_gradient **************** */
+static struct pn_actuator_option_desc cmap_gradient_opts[] =
+{
+  STD_CMAP_OPTS,
+  { "lcolor", "The low color used in the gradient generation",
+    OPT_TYPE_COLOR, { cval: {0, 0, 0} } },
+  { "hcolor", "The high color used in the gradient generation",
+    OPT_TYPE_COLOR, { cval: {0, 0, 0} } },
+  { NULL }
+};
+
+static void
+cmap_gradient_exec (const struct pn_actuator_option *opts,
+		    gpointer data)
+{
+  int i;
+
+  for (i=opts[0].val.ival; i<=opts[1].val.ival; i++)
+    cmap_gen_gradient (((i-opts[0].val.ival)<<8)/(opts[1].val.ival
+						  - opts[0].val.ival),
+		       &opts[2].val.cval, &opts[3].val.cval,
+		       &pn_image_data->cmap[i]);
+}
+
+struct pn_actuator_desc builtin_cmap_gradient =
+{
+  "cmap_gradient",
+  "Normal colourmap",
+  "Sets the colormap to a gradient going from <lcolor> to "
+  "<hcolor>",
+  0, cmap_gradient_opts,
+  NULL, NULL, cmap_gradient_exec
+};
+
+/* **************** cmap_bwgradient **************** */
+static struct pn_actuator_option_desc cmap_bwgradient_opts[] =
+{
+  STD_CMAP_OPTS,
+  { "color", "The intermediate color to use in the gradient",
+    OPT_TYPE_COLOR, { cval: {191, 191, 191} } },
+  { NULL }
+};
+
+static void
+cmap_bwgradient_exec (const struct pn_actuator_option *opts,
+		      gpointer data)
+{
+  int i;
+
+  for (i=opts[0].val.ival; i<128 && i<=opts[1].val.ival; i++)
+    cmap_gen_gradient (i<<1, &black, &opts[2].val.cval,
+		       &pn_image_data->cmap[i]);
+
+  for (i=128; i<256 && i<=opts[1].val.ival; i++)
+    cmap_gen_gradient ((i-128)<<1, &opts[2].val.cval, &white,
+		       &pn_image_data->cmap[i]);
+}
+    
+struct pn_actuator_desc builtin_cmap_bwgradient =
+{
+  "cmap_bwgradient",
+  "Value-based colourmap",
+  "Sets the colormap to a gradient going from black to "
+  "white, via an intermediate color",
+  0, cmap_bwgradient_opts,
+  NULL, NULL, cmap_bwgradient_exec
+};
+
+/* **************** cmap_dynamic **************** */
+static struct pn_actuator_option_desc cmap_dynamic_opts[] =
+{
+  STD_CMAP_OPTS,
+  { "script", "The script to run on each step.",
+    OPT_TYPE_STRING, { sval: "red = red + 0.01; blue = blue + 0.01; green = green + 0.01;" } },
+  { NULL }
+};
+
+typedef struct {
+  expression_t *expr;
+  symbol_dict_t *dict;
+} PnDynamicColourmapData;
+
+static void
+cmap_dynamic_init(gpointer *data)
+{
+  *data = g_new0(PnDynamicColourmapData, 1);
+}
+
+static void
+cmap_dynamic_cleanup(gpointer data)
+{
+  PnDynamicColourmapData *d = (PnDynamicColourmapData *) data;
+
+  if (d->expr)
+    expr_free(d->expr);
+  if (d->dict)
+    dict_free(d->dict);
+
+  g_free(d);
+}
+
+static void
+cmap_dynamic_exec(const struct pn_actuator_option *opts,
+		  gpointer data)
+{
+  PnDynamicColourmapData *d = (PnDynamicColourmapData *) data;
+  gint i, j;
+  gdouble *rf, *bf, *gf, *inf;
+  gint rn, bn, gn;
+
+  if (!d->dict && !d->expr)
+    {
+       d->dict = dict_new();
+       if (!d->dict)
+         return;
+
+       d->expr = expr_compile_string(opts[2].val.sval, d->dict);
+       if (!d->expr)
+         {
+           dict_free(d->dict);
+           d->dict = NULL;
+           return;
+         }
+    }
+
+  rf = dict_variable(d->dict, "red");
+  gf = dict_variable(d->dict, "green");
+  bf = dict_variable(d->dict, "blue");
+  inf = dict_variable(d->dict, "index");
+
+  for (i = opts[0].val.ival; i < 255 && i <= opts[1].val.ival; i++)
+    {
+       *inf = ((gdouble)i / 255.0);
+
+       expr_execute(d->expr, d->dict);
+
+       /* Convert rf/bf/gf to realworld values. */
+       rn = (gdouble)(*rf * 255);
+       gn = (gdouble)(*gf * 255);
+       bn = (gdouble)(*bf * 255);
+
+       pn_image_data->cmap[i].r = rn;
+       pn_image_data->cmap[i].g = gn;
+       pn_image_data->cmap[i].b = bn;
+    }
+}
+
+struct pn_actuator_desc builtin_cmap_dynamic =
+{
+  "cmap_dynamic",
+  "Dynamic Colourmap",
+  "Scriptable colourmap modifier.",
+  0, cmap_dynamic_opts,
+  cmap_dynamic_init, cmap_dynamic_cleanup, cmap_dynamic_exec
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/containers.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,250 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "actuators.h"
+#include "paranormal.h"
+
+/* **************** all containers **************** */
+
+/* Add a actuator to a container (end of list) */
+void
+container_add_actuator (struct pn_actuator *container, struct pn_actuator *a)
+{
+  g_assert (container->desc->flags & ACTUATOR_FLAG_CONTAINER);
+  g_assert (a);
+
+  *((GSList **)container->data) =
+    g_slist_append (*(GSList **)container->data, a);
+}  
+
+void
+container_remove_actuator (struct pn_actuator *container, struct pn_actuator *a)
+{
+  g_assert (container->desc->flags & ACTUATOR_FLAG_CONTAINER);
+  g_assert (a);
+
+  *((GSList **)container->data) =
+    g_slist_remove (*(GSList **)container->data, a);
+} 
+
+/* clear the containee list */
+void
+container_unlink_actuators (struct pn_actuator *container)
+{
+  g_assert (container->desc->flags & ACTUATOR_FLAG_CONTAINER);
+
+  g_slist_free (*(GSList **)container->data);
+  *(GSList **)container->data = NULL;
+}
+
+/* this does NOT free data */
+static void
+container_cleanup (GSList** data)
+{
+  GSList *child;
+
+  for (child = *data; child; child = child->next)
+    destroy_actuator ((struct pn_actuator *) child->data);
+
+  g_slist_free (*data);
+}
+
+/* **************** container_simple **************** */
+static void
+container_simple_init (GSList ***data)
+{
+  *data = g_new0 (GSList *, 1);
+}
+
+static void
+container_simple_cleanup (GSList **data)
+{
+  container_cleanup (data);
+  g_free (data);
+}
+
+static void
+container_simple_exec (const struct pn_actuator_option *opts,
+		     GSList **data)
+{
+  GSList *child;
+
+  for (child = *data; child; child = child->next)
+    exec_actuator ((struct pn_actuator *) child->data);
+}
+
+struct pn_actuator_desc builtin_container_simple =
+{
+  "container_simple",
+  "Simple Container",
+  "A simple (unconditional) container\n\n"
+  "This is usually used as the root actuator of a list",
+  ACTUATOR_FLAG_CONTAINER, NULL,
+  PN_ACTUATOR_INIT_FUNC (container_simple_init),
+  PN_ACTUATOR_CLEANUP_FUNC (container_simple_cleanup),
+  PN_ACTUATOR_EXEC_FUNC (container_simple_exec)
+};
+
+/* **************** container_once **************** */
+struct container_once_data
+{
+  GSList *children; /* This MUST be first! */
+
+  gboolean done;
+};
+
+static void
+container_once_init (struct container_once_data **data)
+{
+  *data = g_new0 (struct container_once_data, 1);
+}
+
+static void
+container_once_cleanup (GSList **data)
+{
+  container_cleanup (data);
+  g_free (data);
+}
+
+static void
+container_once_exec (const struct pn_actuator_option *opts,
+		     struct container_once_data *data)
+{
+  if (! data->done)
+    {
+      GSList *child;
+
+      for (child = data->children; child; child = child->next)
+	exec_actuator ((struct pn_actuator *) child->data);
+
+      data->done = TRUE;
+    }
+}
+
+struct pn_actuator_desc builtin_container_once =
+{
+  "container_once",
+  "Initialization Container",
+  "A container whose contents get executed exactly once.\n\n"
+  "This is often used to set initial graphics states such as the\n"
+  "pixel depth, or to display some text (such as credits)",
+  ACTUATOR_FLAG_CONTAINER, NULL,
+  PN_ACTUATOR_INIT_FUNC (container_once_init),
+  PN_ACTUATOR_CLEANUP_FUNC (container_once_cleanup),
+  PN_ACTUATOR_EXEC_FUNC (container_once_exec)
+};
+
+/* **************** container_cycle ***************** */
+static struct pn_actuator_option_desc container_cycle_opts[] =
+{
+  { "change_interval", "The number of seconds between changing the "
+    "child to be executed", OPT_TYPE_INT, { ival: 20 } },
+  { "beat", "Whether or not the change should only occur on a beat",
+    OPT_TYPE_BOOLEAN, { bval: TRUE } },
+  { NULL }
+};
+
+struct container_cycle_data
+{
+  GSList *children;
+  GSList *current;
+  int last_change;
+};
+
+static void
+container_cycle_init (gpointer *data)
+{
+  *data = g_new0 (struct container_cycle_data, 1);
+}
+
+static void
+container_cycle_cleanup (gpointer data)
+{
+  container_cleanup (data);
+  g_free (data);
+}
+
+static void
+container_cycle_exec (const struct pn_actuator_option *opts,
+		      gpointer data)
+{
+  struct container_cycle_data *cdata = (struct container_cycle_data*)data;
+  int now;
+
+  /*
+   * Change branch if all of the requirements are met for the branch to change.
+   */
+  if ((opts[1].val.bval == TRUE && pn_new_beat != FALSE) || opts[1].val.bval == FALSE)
+    {
+       now = SDL_GetTicks();	
+
+       if (now - cdata->last_change
+           > opts[0].val.ival * 1000)
+         {
+            cdata->last_change = now;
+
+            /* FIXME: add randomization support */
+            if (cdata->current)
+              cdata->current = cdata->current->next;
+         }
+    }
+
+  if (! cdata->current)
+    cdata->current = cdata->children;
+
+  if (cdata->current)
+    exec_actuator ((struct pn_actuator*)cdata->current->data);
+}
+
+struct pn_actuator_desc builtin_container_cycle =
+{
+  "container_cycle",
+  "Branched-execution Container",
+  "A container that alternates which of its children is executed;  it "
+  "can either change on an interval, or only on a beat.",
+  ACTUATOR_FLAG_CONTAINER, container_cycle_opts,
+  container_cycle_init, container_cycle_cleanup, container_cycle_exec
+};
+
+/* **************** container_onbeat **************** */
+static void
+container_onbeat_init (GSList ***data)
+{
+  *data = g_new0 (GSList *, 1);
+}
+
+static void
+container_onbeat_cleanup (GSList **data)
+{
+  container_cleanup (data);
+  g_free (data);
+}
+
+static void
+container_onbeat_exec (const struct pn_actuator_option *opts,
+		     GSList **data)
+{
+  GSList *child;
+
+  if (pn_new_beat == TRUE)
+    {
+      for (child = *data; child; child = child->next)
+        exec_actuator ((struct pn_actuator *) child->data);
+    }
+}
+
+struct pn_actuator_desc builtin_container_onbeat =
+{
+  "container_onbeat",
+  "OnBeat Container",
+  "A simple container which only triggers on a beat.",
+  ACTUATOR_FLAG_CONTAINER, NULL,
+  PN_ACTUATOR_INIT_FUNC (container_onbeat_init),
+  PN_ACTUATOR_CLEANUP_FUNC (container_onbeat_cleanup),
+  PN_ACTUATOR_EXEC_FUNC (container_onbeat_exec)
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/containers.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,8 @@
+#ifndef _CONTAINERS_H
+#define _CONTAINERS_H
+
+void container_add_actuator (struct pn_actuator *container, struct pn_actuator *a);
+void container_remove_actuator (struct pn_actuator *container, struct pn_actuator *a);
+void container_unlink_actuators (struct pn_actuator *container);
+
+#endif /* _CONTAINERS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/drawing.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,45 @@
+#include <math.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+#include "pn_utils.h"
+
+extern SDL_Surface *screen;
+
+void
+pn_draw_dot (guint x, guint y, guchar value)
+{
+  if (x > pn_image_data->width || x < 0 || y > pn_image_data->height || y < 0)
+    return;
+
+  pn_image_data->surface[0][PN_IMG_INDEX(x, y)] = value;
+}
+
+void
+pn_draw_line (guint _x0, guint _y0, guint _x1, guint _y1, guchar value)
+{
+  gint x0 = _x0;
+  gint y0 = _y0;
+  gint x1 = _x1;
+  gint y1 = _y1;
+
+  gint dx = x1 - x0;
+  gint dy = y1 - y0;
+
+  pn_draw_dot(x0, y0, value);
+
+  if (dx != 0)
+    {
+      gfloat m = (gfloat) dy / (gfloat) dx;
+      gfloat b = y0 - m * x0;
+
+      dx = (x1 > x0) ? 1 : - 1;
+      while (x0 != x1)
+        {
+          x0 += dx;
+          y0 = m * x0 + b;
+
+          pn_draw_dot(x0, y0, value);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/drawing.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,9 @@
+#include <glib.h>
+
+#ifndef PN_DRAWING_H
+#define PN_DRAWING_H
+
+void pn_draw_dot (guint x, guint y, guchar value);
+void pn_draw_line (guint _x0, guint _y0, guint _x1, guint _y1, guchar value);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/freq.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,64 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+#include "pn_utils.h"
+
+/* **************** freq_dots **************** */
+/* FIXME: take this piece of crap out */
+static void
+freq_dots_exec (const struct pn_actuator_option *opts,
+		gpointer data)
+{
+  int i, basex;
+
+  basex = (pn_image_data->width>>1)-128;
+  for (i=basex < 0 ? -basex : 0 ; i < 256; i++)
+    {
+      pn_image_data->surface[0][PN_IMG_INDEX (basex+i, (pn_image_data->height>>1)
+					      - CAP (pn_sound_data->freq_data[0][i], 120))]
+	= 0xff;
+      pn_image_data->surface[0][PN_IMG_INDEX (basex+256-i, (pn_image_data->height>>1)
+					      + CAP (pn_sound_data->freq_data[1][i], 120))]
+	= 0xff;
+    }
+}
+
+struct pn_actuator_desc builtin_freq_dots =
+{
+  "freq_dots",
+  "Frequency Scope",
+  "Draws dots varying vertically with the freqency data.",
+  0, NULL,
+  NULL, NULL, freq_dots_exec
+};
+
+/* **************** freq_drops **************** */
+static void
+freq_drops_exec (const struct pn_actuator_option *opts,
+		gpointer data)
+{
+  int i,j;
+  
+  for (i=0; i<256; i++)
+    for (j=0; j<pn_sound_data->freq_data[0][i]>>3; i++)
+      pn_image_data->surface[0][PN_IMG_INDEX (rand() % pn_image_data->width,
+					      rand() % pn_image_data->height)]
+	= 0xff;
+}
+
+struct pn_actuator_desc builtin_freq_drops =
+{
+  "freq_drops",
+  "Random Dots",
+  "Draws dots at random on the image (louder music = more dots)",
+  0, NULL,
+  NULL, NULL, freq_drops_exec
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/general.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,311 @@
+/* FIXME: what to name this file? */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "paranormal.h"
+#include "actuators.h"
+#include "pn_utils.h"
+
+/* **************** general_fade **************** */
+static struct pn_actuator_option_desc general_fade_opts[] =
+{
+  { "amount", "The amount by which the color index of each "
+    "pixel should be decreased by each frame (MAX 255)",
+    OPT_TYPE_INT, { ival: 3 } },
+  { NULL }
+};
+
+static void
+general_fade_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  int amt = opts[0].val.ival > 255 || opts[0].val.ival < 0 ? 3 : opts[0].val.ival;
+  int i, j;
+
+  for (j=0; j<pn_image_data->height; j++)
+    for (i=0; i<pn_image_data->width; i++)
+      pn_image_data->surface[0][PN_IMG_INDEX (i, j)] =
+	CAPLO (pn_image_data->surface[0][PN_IMG_INDEX (i, j)]
+	       - amt, 0);
+}
+
+struct pn_actuator_desc builtin_general_fade =
+{
+  "general_fade", "Fade-out", "Decreases the color index of each pixel",
+  0, general_fade_opts,
+  NULL, NULL, general_fade_exec
+};
+
+/* **************** general_blur **************** */
+/* FIXME: add a variable radius */
+/* FIXME: SPEEEED */
+static void
+general_blur_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  int i,j;
+  register guchar *srcptr = pn_image_data->surface[0];
+  register guchar *destptr = pn_image_data->surface[1];
+  register int sum;
+
+  for (j=0; j<pn_image_data->height; j++)
+    for (i=0; i<pn_image_data->width; i++)
+      {
+	sum = *(srcptr)<<2;
+
+	/* top */
+	if (j > 0)
+	  {
+	    sum += *(srcptr-pn_image_data->width)<<1;
+	    if (i > 0)
+	      sum += *(srcptr-pn_image_data->width-1);
+	    if (i < pn_image_data->width-1)
+	      sum += *(srcptr-pn_image_data->width+1);
+	  }
+	/* bottom */
+	if (j < pn_image_data->height-1)
+	  {
+	    sum += *(srcptr+pn_image_data->width)<<1;
+	    if (i > 0)
+	      sum += *(srcptr+pn_image_data->width-1);
+	    if (i < pn_image_data->width-1)
+	      sum += *(srcptr+pn_image_data->width+1);
+	  }
+	/* left */
+	if (i > 0)
+	  sum += *(srcptr-1)<<1;
+	/* right */
+	if (i < pn_image_data->width-1)
+	  sum += *(srcptr+1)<<1;
+
+	*destptr++ = (guchar)(sum >> 4);
+	srcptr++;
+      }
+
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_general_blur = 
+{
+  "general_blur", "Blur", "A simple 1 pixel radius blur",
+  0, NULL,
+  NULL, NULL, general_blur_exec
+};
+
+/* **************** general_mosaic **************** */
+/* FIXME: add a variable radius */
+/* FIXME: SPEEEED */
+static struct pn_actuator_option_desc general_mosaic_opts[] =
+{
+  { "radius", "The pixel radius that should be used for the effect.",
+    OPT_TYPE_INT, { ival: 6 } },
+  { NULL }
+};
+
+static void
+general_mosaic_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  int i,j;
+  register guchar *srcptr = pn_image_data->surface[0];
+  register guchar *destptr = pn_image_data->surface[1];
+  register int sum;
+  int radius = opts[0].val.ival > 255 || opts[0].val.ival < 0 ? 6 : opts[0].val.ival;
+
+  for (j=0; j<pn_image_data->height; j += radius)
+    for (i=0; i<pn_image_data->width; i += radius)
+      {
+        int ii = 0, jj = 0;
+        guchar bval = 0;
+
+        /* find the brightest colour */
+        for (jj = 0; jj < radius && (j + jj < pn_image_data->height); jj++)
+          for (ii = 0; ii < radius && (i + ii < pn_image_data->width); ii++)
+            {
+               guchar val = srcptr[PN_IMG_INDEX(i + ii,  j + jj)];
+
+               if (val > bval)
+                 bval = val;
+            }
+
+        for (jj = 0; jj < radius && (j + jj < pn_image_data->height); jj++)
+          for (ii = 0; ii < radius && (i + ii < pn_image_data->width); ii++)
+            {
+               destptr[PN_IMG_INDEX(i + ii, j + jj)] = bval;
+            }
+      }
+
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_general_mosaic = 
+{
+  "general_mosaic", "Mosaic", "A simple mosaic effect.",
+  0, general_mosaic_opts,
+  NULL, NULL, general_mosaic_exec
+};
+
+/* **************** general_clear **************** */
+static void
+general_clear_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+   memset(pn_image_data->surface[0], '\0',
+	  (pn_image_data->height * pn_image_data->width));
+}
+
+struct pn_actuator_desc builtin_general_clear =
+{
+  "general_clear", "Clear Surface", "Clears the surface.",
+  0, NULL,
+  NULL, NULL, general_clear_exec
+};
+
+/* **************** general_noop **************** */
+static void
+general_noop_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  return;
+}
+
+struct pn_actuator_desc builtin_general_noop =
+{
+  "general_noop", "Do Nothing", "Does absolutely nothing.",
+  0, NULL,
+  NULL, NULL, general_noop_exec
+};
+
+/* **************** general_invert **************** */
+static void
+general_invert_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  int i, j;
+
+  for (j=0; j < pn_image_data->height; j++)
+    for (i=0; i < pn_image_data->width; i++)
+      pn_image_data->surface[0][PN_IMG_INDEX (i, j)] =
+	255 - pn_image_data->surface[0][PN_IMG_INDEX (i, j)];
+}
+
+struct pn_actuator_desc builtin_general_invert =
+{
+  "general_invert", "Value Invert", "Performs a value invert.",
+  0, NULL,
+  NULL, NULL, general_invert_exec
+};
+
+/* **************** general_replace **************** */
+static struct pn_actuator_option_desc general_replace_opts[] =
+{
+  { "start", "The beginning colour value that should be replaced by the value of out.",
+    OPT_TYPE_INT, { ival: 250 } },
+  { "end", "The ending colour value that should be replaced by the value of out.",
+    OPT_TYPE_INT, { ival: 255 } },
+  { "out", "The colour value that in is replaced with.",
+    OPT_TYPE_INT, { ival: 0 } },
+  { NULL }
+};
+
+static void
+general_replace_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  register int i, j;
+  register guchar val;
+  guchar begin = opts[0].val.ival > 255 || opts[0].val.ival < 0 ? 250 : opts[0].val.ival;
+  guchar end = opts[1].val.ival > 255 || opts[1].val.ival < 0 ? 255 : opts[1].val.ival;
+  guchar out = opts[2].val.ival > 255 || opts[2].val.ival < 0 ? 0 : opts[2].val.ival;
+
+  for (j=0; j < pn_image_data->height; j++)
+    for (i=0; i < pn_image_data->width; i++)
+      {
+        val = pn_image_data->surface[0][PN_IMG_INDEX (i, j)];
+        if (val >= begin && val <= end)
+          pn_image_data->surface[0][PN_IMG_INDEX (i, j)] = out;
+      }
+}
+
+struct pn_actuator_desc builtin_general_replace =
+{
+  "general_replace", "Value Replace", "Performs a value replace on a range of values.",
+  0, general_replace_opts,
+  NULL, NULL, general_replace_exec
+};
+
+/* **************** general_swap **************** */
+static void
+general_swap_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_general_swap =
+{
+  "general_swap", "Swap Surface", "Swaps the surface.",
+  0, NULL,
+  NULL, NULL, general_swap_exec
+};
+
+/* **************** general_copy **************** */
+static void
+general_copy_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  memcpy(pn_image_data->surface[1], pn_image_data->surface[0], 
+         (pn_image_data->width * pn_image_data->height));
+}
+
+struct pn_actuator_desc builtin_general_copy =
+{
+  "general_copy", "Copy Surface", "Copies the surface to the other surface.",
+  0, NULL,
+  NULL, NULL, general_copy_exec
+};
+
+/* **************** general_flip **************** */
+static struct pn_actuator_option_desc general_flip_opts[] =
+{
+  { "direction", "Negative is horizontal, positive is vertical.",
+    OPT_TYPE_INT, { ival: -1 } },
+  { NULL }
+};
+
+static void
+general_flip_exec (const struct pn_actuator_option *opts,
+	   gpointer data)
+{
+  gint x, y;
+
+  if (opts[0].val.ival < 0)
+    {
+      for (y = 0; y < pn_image_data->height; y++)
+        for (x = 0; x < pn_image_data->width; x++)
+          {
+             pn_image_data->surface[1][PN_IMG_INDEX(pn_image_data->width - x, y)] = 
+               pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
+          }
+    }
+  else
+    {
+      for (y = 0; y < pn_image_data->height; y++)
+        for (x = 0; x < pn_image_data->width; x++)
+          {
+             pn_image_data->surface[1][PN_IMG_INDEX(x, pn_image_data->height - y)] = 
+               pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
+          }
+    }
+
+    pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_general_flip =
+{
+  "general_flip", "Flip Surface", "Flips the surface.",
+  0, general_flip_opts,
+  NULL, NULL, general_flip_exec
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/Makefile	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,19 @@
+include ../../../mk/rules.mk
+include ../../../mk/init.mk
+
+OBJECTIVE_LIBS_NOINST = libcalc.a
+
+LIBDIR = $(plugindir)/$(VISUALIZATION_PLUGIN_DIR)
+
+LIBADD = $(GTK_LIBS) $(XML_LIBS) $(SDL_LIBS)
+
+SOURCES = dict.c  execute.c  function.c  parser.c  storage.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) $(ARCH_DEFINES) $(XML_CPPFLAGS) $(SDL_CFLAGS) -I../../intl -I../..
+
+include ../../../mk/objective.mk
+
+libcalc.a: $(OBJECTS)
+	$(AR) cq $@ $(OBJECTS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/calc.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,30 @@
+/* calc.h -- an all-in-one include file for libcalc
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+
+#ifndef Included_CALC_H
+#define Included_CALC_H
+
+#include "dict.h"
+#include "execute.h"
+#include "parser.h"
+#include "storage.h"
+
+#endif /* Included_CALC_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/dict.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,99 @@
+/* dict.c -- symbol dictionary structures
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+ 
+#define V_SPACE_INIT 8
+#define V_SPACE_INCR 8
+ 
+#include <glib.h>
+#include <string.h>
+
+#include "dict.h"
+
+static void more_variables (symbol_dict_t *dict) {
+  var_t *new_var;
+
+  dict->v_space += V_SPACE_INCR;
+
+  new_var = (var_t *)g_malloc (dict->v_space * sizeof(var_t));
+
+  memcpy (new_var, dict->variables, dict->v_count * sizeof(var_t));
+  g_free (dict->variables);
+
+  dict->variables = new_var;
+}
+
+symbol_dict_t *dict_new (void) {
+  symbol_dict_t *dict;
+  
+  dict = (symbol_dict_t *) g_malloc (sizeof(symbol_dict_t));
+
+  /* Allocate space for variables. */
+  dict->v_count = 0;
+  dict->v_space = V_SPACE_INIT;
+  dict->variables = (var_t *)g_malloc (dict->v_space * sizeof(var_t));
+  
+  return dict;
+}
+
+void dict_free (symbol_dict_t *dict) {
+  int i;
+
+  if (!dict)
+    return;
+
+  /* Free memory used by variables. */
+  for (i = 0; i < dict->v_count; i++)
+    g_free (dict->variables[i].name);
+  g_free (dict->variables);
+
+  g_free (dict);
+}
+
+static int dict_define_variable (symbol_dict_t *dict, const char *name) {
+  var_t *var;
+
+  if (dict->v_count >= dict->v_space)
+    more_variables (dict);
+
+  var = &dict->variables[dict->v_count];
+
+  var->value = 0.0;
+  var->name = g_strdup (name);
+
+  return dict->v_count++;
+}
+
+int dict_lookup (symbol_dict_t *dict, const char *name) {
+  int i;
+
+  for (i = 0; i < dict->v_count; i++) {
+    if (strcmp (dict->variables[i].name, name) == 0)
+      return i;
+  }
+  
+  /* Not defined -- define a new variable. */
+  return dict_define_variable (dict, name);
+}
+
+double *dict_variable (symbol_dict_t *dict, const char *var_name) {
+  int id = dict_lookup (dict, var_name);
+  return &dict->variables[id].value;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/dict.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,46 @@
+/* dict.h -- symbol dictionary structures
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+
+#ifndef Included_DICT_H
+#define Included_DICT_H
+
+/* A variable. */
+typedef struct {
+  char *name;
+  double value;
+} var_t;
+
+/* A symbol dictionary. */
+typedef struct {
+  /* Variables. */
+  var_t *variables;
+  int v_count;
+  int v_space;
+} symbol_dict_t;
+
+/* Prototypes. */
+symbol_dict_t *dict_new (void);
+void dict_free (symbol_dict_t *dict);
+
+int dict_lookup (symbol_dict_t *calc, const char *name);
+double* dict_variable (symbol_dict_t *calc, const char *var_name);
+
+#endif /* Included_DICT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/execute.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,121 @@
+/* execute.c -- execute precompiled expression expr
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+ 
+#include <glib.h>
+#include <math.h>
+ 
+#include "execute.h"
+#include "function.h"
+
+/* Execution stack. */
+
+gboolean check_stack (ex_stack *stack, int depth) {
+  if (stack->sp < depth) {
+    g_warning ("Stack error");
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+void push (ex_stack *stack, double value) {
+  g_assert (stack);
+
+  if (stack->sp < STACK_DEPTH) {
+    stack->value[stack->sp++] = value;
+  } else {
+    g_warning ("Stack overflow");
+  }
+}
+
+double pop (ex_stack *stack) {
+  g_assert (stack);
+
+  if (stack->sp > 0) {
+    return stack->value[--stack->sp];
+  } else {
+    g_warning ("Stack error (stack empty)");
+    return 0.0;
+  }
+}
+
+/* */
+
+void expr_execute (expression_t *expr, symbol_dict_t *dict) {
+  char op, *str = expr->data->str;
+  ex_stack stack = { 0, { 0.0 }};
+
+  while ((op = *str++)) {
+    switch (op) { 
+    case 'l': /* Load a variable. */
+      push (&stack, dict->variables[load_int (str)].value);
+      str += sizeof (int);
+      break;
+      
+    case 's': /* Store to a variable. */
+      dict->variables[load_int (str)].value = pop (&stack);
+      str += sizeof (int);
+      break;
+   
+    case 'f': /* Call a function. */
+      function_call (load_int (str), &stack);
+      str += sizeof (int);
+      break;
+      
+    case 'c': /* Load a constant. */
+      push (&stack, load_double (str));
+      str += sizeof (double);
+      break;
+
+    case 'n': /* Do a negation. */
+      push (&stack, -pop (&stack));
+      break;
+
+    case '+': /* Do an addition. */
+      push (&stack, pop (&stack) + pop (&stack));
+      break;
+    case '-': /* Do a subtraction. */
+      push (&stack, pop (&stack) - pop (&stack));
+      break;
+    case '*': /* Do a multiplication. */
+      push (&stack, pop (&stack) * pop (&stack));
+      break;
+    case '/': /* Do a division. */
+      if (check_stack (&stack, 2)) {
+        double y = stack.value[stack.sp - 2] / stack.value[stack.sp - 1];
+	stack.sp -= 2;
+	push (&stack, y);
+      }
+      break;
+    case '^': /* Do an exponentiation. */
+      if (check_stack (&stack, 2)) {
+        double y = pow (stack.value[stack.sp - 2], stack.value[stack.sp - 1]);
+	stack.sp -= 2;
+	push (&stack, y);
+      }
+      break;
+
+    default:
+      g_warning ("Invalid opcode: %c", op);
+      return;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/execute.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,46 @@
+/* execute.h -- execute precompiled expression code
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+
+#ifndef Included_EXECUTE_H
+#define Included_EXECUTE_H
+
+#include <glib.h>
+
+#include "dict.h"
+#include "storage.h"
+
+/* Execution stack. */
+
+typedef struct { 
+#define STACK_DEPTH 64
+  int sp;			/* stack pointer */
+  double value[STACK_DEPTH];
+} ex_stack;
+
+/* Prototypes. */
+
+gboolean check_stack (ex_stack *stack, int depth);
+void push (ex_stack *stack, double value);
+double pop (ex_stack *stack);
+
+void expr_execute (expression_t *expr, symbol_dict_t *dict);
+
+#endif /* Included_EXECUTE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/function.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,106 @@
+/* function.c --
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+ 
+#include <glib.h>
+#include <math.h>
+#include <string.h>
+ 
+#include "function.h"
+
+/* Function pointer type. */
+typedef struct {
+  char *name;
+  double (*funcptr)(ex_stack *stack);
+} func_t;
+
+/* */
+
+static double f_log (ex_stack *stack) {
+  return log (pop (stack));
+}
+
+static double f_sin (ex_stack *stack) {
+  return sin (pop (stack));
+}
+
+static double f_cos (ex_stack *stack) {
+  return cos (pop (stack));
+}
+
+static double f_tan (ex_stack *stack) {
+  return tan (pop (stack));
+}
+
+static double f_asin (ex_stack *stack) {
+  return asin (pop (stack));
+}
+
+static double f_acos (ex_stack *stack) {
+  return acos (pop (stack));
+}
+
+static double f_atan (ex_stack *stack) {
+  return atan (pop (stack));
+}
+
+static double f_if (ex_stack *stack) {
+  double a = pop (stack);
+  double b = pop (stack);
+  return (pop (stack) != 0.0) ? a : b;
+}
+
+static double f_div (ex_stack *stack) {
+  int y = (int)pop (stack);
+  int x = (int)pop (stack);
+  return (y == 0) ? 0 : (x / y);
+}
+
+/* */
+
+static const func_t init[] = {
+  { "sin", f_sin },
+  { "cos", f_cos },
+  { "tan", f_tan },
+  { "asin", f_asin },
+  { "acos", f_acos },
+  { "atan", f_atan },
+  { "log", f_log },
+  { "if", f_if },
+  { "div", f_div }
+};
+
+int function_lookup (const char *name) {
+  int i;
+
+  for (i = 0; i < sizeof (init) / sizeof (init[0]); i++)
+    if (strcmp (init[i].name, name) == 0)
+      return i;
+
+  g_warning ("Unknown function: %s\n", name);
+  return -1;
+}
+
+void function_call (int func_id, ex_stack *stack) {
+  g_assert (func_id >= 0);
+  g_assert (func_id < sizeof (init) / sizeof (init[0]));
+
+  push (stack, (*init[func_id].funcptr)(stack));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/function.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,32 @@
+/* function.h --
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+
+#ifndef Included_FUNCTION
+#define Included_FUNCTION
+
+#include "execute.h"
+
+/* Prototypes. */
+
+int function_lookup (const char *name);
+void function_call (int func_id, ex_stack *stack);
+
+#endif /* Included_FUNCTION */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/parser.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,1521 @@
+/* A Bison parser, made by GNU Bison 1.875d.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+   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, 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.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     NAME = 258,
+     NUMBER = 259,
+     NEG = 260
+   };
+#endif
+#define NAME 258
+#define NUMBER 259
+#define NEG 260
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 26 "parser.y"
+
+#include <ctype.h>
+#include <glib.h>
+#include <locale.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "dict.h"
+#include "execute.h"
+#include "function.h"
+#include "parser.h"
+#include "storage.h"
+
+#define YYPARSE_PARAM yyparam
+#define YYLEX_PARAM yyparam
+
+static gboolean expr_add_compile (expression_t *expr, symbol_dict_t *dict,
+				  char *str);
+
+#define GENERATE(str) if (!expr_add_compile (((parser_control *)yyparam)->expr, \
+				        ((parser_control *)yyparam)->dict, str)) \
+                   YYABORT;
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 54 "parser.y"
+typedef union YYSTYPE {
+char *s_value;
+char c_value;
+double d_value;
+int i_value;
+} YYSTYPE;
+/* Line 191 of yacc.c.  */
+#line 118 "parser.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 214 of yacc.c.  */
+#line 130 "parser.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+#  define YYFREE free
+# endif
+# ifndef YYMALLOC
+#  define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   define YYSTACK_ALLOC alloca
+#  endif
+# else
+#  if defined (alloca) || defined (_ALLOCA_H)
+#   define YYSTACK_ALLOC alloca
+#  else
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+	 || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short int yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short int) + sizeof (YYSTYPE))			\
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined (__GNUC__) && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  register YYSIZE_T yyi;		\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   65
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  18
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  5
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  22
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  37
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   260
+
+#define YYTRANSLATE(YYX) 						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const unsigned char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+      14,    15,     8,     7,    13,     6,     2,     9,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,    12,
+      17,     5,    16,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,    11,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+      10
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned char yyprhs[] =
+{
+       0,     0,     3,     4,     7,     8,    10,    13,    16,    18,
+      22,    24,    26,    30,    35,    39,    43,    47,    51,    55,
+      59,    62,    66
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+      19,     0,    -1,    -1,    19,    20,    -1,    -1,    22,    -1,
+      20,    12,    -1,     1,    12,    -1,    22,    -1,    21,    13,
+      22,    -1,     4,    -1,     3,    -1,     3,     5,    22,    -1,
+       3,    14,    21,    15,    -1,    22,    16,    22,    -1,    22,
+      17,    22,    -1,    22,     7,    22,    -1,    22,     6,    22,
+      -1,    22,     8,    22,    -1,    22,     9,    22,    -1,     6,
+      22,    -1,    22,    11,    22,    -1,    14,    22,    15,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned char yyrline[] =
+{
+       0,    76,    76,    77,    81,    82,    84,    85,    90,    93,
+      98,   104,   110,   116,   123,   125,   128,   130,   132,   134,
+     136,   138,   140
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "NAME", "NUMBER", "'='", "'-'", "'+'",
+  "'*'", "'/'", "NEG", "'^'", "';'", "','", "'('", "')'", "'>'", "'<'",
+  "$accept", "input", "expression_list", "argument_list", "expression", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short int yytoknum[] =
+{
+       0,   256,   257,   258,   259,    61,    45,    43,    42,    47,
+     260,    94,    59,    44,    40,    41,    62,    60
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,    18,    19,    19,    20,    20,    20,    20,    21,    21,
+      22,    22,    22,    22,    22,    22,    22,    22,    22,    22,
+      22,    22,    22
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     0,     2,     0,     1,     2,     2,     1,     3,
+       1,     1,     3,     4,     3,     3,     3,     3,     3,     3,
+       2,     3,     3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const unsigned char yydefact[] =
+{
+       2,     0,     1,     0,    11,    10,     0,     0,     3,     5,
+       7,     0,     0,    20,     0,     6,     0,     0,     0,     0,
+       0,     0,     0,    12,     0,     8,    22,    17,    16,    18,
+      19,    21,    14,    15,     0,    13,     9
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+      -1,     1,     8,    24,     9
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -10
+static const yysigned_char yypact[] =
+{
+     -10,    17,   -10,    -8,    22,   -10,    47,    47,    -3,    38,
+     -10,    47,    47,    -9,    26,   -10,    47,    47,    47,    47,
+      47,    47,    47,    38,     9,    38,   -10,    48,    48,    -9,
+      -9,    -9,    38,    38,    47,   -10,    38
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yysigned_char yypgoto[] =
+{
+     -10,   -10,   -10,   -10,    -6
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -5
+static const yysigned_char yytable[] =
+{
+      13,    14,    20,     0,    10,    23,    25,    21,    22,    15,
+      27,    28,    29,    30,    31,    32,    33,     2,     3,     0,
+       4,     5,    34,     6,    35,     0,     0,    11,    36,    -4,
+       0,     7,    16,    17,    18,    19,    12,    20,     0,     0,
+       0,    26,    21,    22,    16,    17,    18,    19,     0,    20,
+       4,     5,     0,     6,    21,    22,    18,    19,     0,    20,
+       0,     7,     0,     0,    21,    22
+};
+
+static const yysigned_char yycheck[] =
+{
+       6,     7,    11,    -1,    12,    11,    12,    16,    17,    12,
+      16,    17,    18,    19,    20,    21,    22,     0,     1,    -1,
+       3,     4,    13,     6,    15,    -1,    -1,     5,    34,    12,
+      -1,    14,     6,     7,     8,     9,    14,    11,    -1,    -1,
+      -1,    15,    16,    17,     6,     7,     8,     9,    -1,    11,
+       3,     4,    -1,     6,    16,    17,     8,     9,    -1,    11,
+      -1,    14,    -1,    -1,    16,    17
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,    19,     0,     1,     3,     4,     6,    14,    20,    22,
+      12,     5,    14,    22,    22,    12,     6,     7,     8,     9,
+      11,    16,    17,    22,    21,    22,    15,    22,    22,    22,
+      22,    22,    22,    22,    13,    15,    22
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { 								\
+      yyerror ("syntax error: cannot back up");\
+      YYERROR;							\
+    }								\
+while (0)
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+   are run).  */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)		\
+   ((Current).first_line   = (Rhs)[1].first_line,	\
+    (Current).first_column = (Rhs)[1].first_column,	\
+    (Current).last_line    = (Rhs)[N].last_line,	\
+    (Current).last_column  = (Rhs)[N].last_column)
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (0)
+
+# define YYDSYMPRINT(Args)			\
+do {						\
+  if (yydebug)					\
+    yysymprint Args;				\
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location)		\
+do {								\
+  if (yydebug)							\
+    {								\
+      YYFPRINTF (stderr, "%s ", Title);				\
+      yysymprint (stderr, 					\
+                  Token, Value);	\
+      YYFPRINTF (stderr, "\n");					\
+    }								\
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short int *bottom;
+    short int *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+             yyrule - 1, yylno);
+  /* Print the symbols being reduced, and their result.  */
+  for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+    YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+  YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (Rule);		\
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (yytype < YYNTOKENS)
+    {
+      YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+      YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+    }
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  switch (yytype)
+    {
+
+      default:
+        break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  /* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  short int yyssa[YYINITDEPTH];
+  short int *yyss = yyssa;
+  register short int *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack. Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	short int *yyss1 = yyss;
+
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow ("parser stack overflow",
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+
+		    &yystacksize);
+
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyoverflowlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	short int *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyoverflowlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+  YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 5:
+#line 83 "parser.y"
+    { ;}
+    break;
+
+  case 7:
+#line 86 "parser.y"
+    { yyerrok; ;}
+    break;
+
+  case 8:
+#line 91 "parser.y"
+    {
+                            ;}
+    break;
+
+  case 9:
+#line 94 "parser.y"
+    {
+                            ;}
+    break;
+
+  case 10:
+#line 99 "parser.y"
+    { 
+                              char *buf = g_strdup_printf ("c%f:", yyvsp[0].d_value);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            ;}
+    break;
+
+  case 11:
+#line 105 "parser.y"
+    { 
+                              char *buf = g_strdup_printf ("l%s:", yyvsp[0].s_value);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            ;}
+    break;
+
+  case 12:
+#line 111 "parser.y"
+    { 
+                              char *buf = g_strdup_printf ("s%s:", yyvsp[-2].s_value);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            ;}
+    break;
+
+  case 13:
+#line 117 "parser.y"
+    {
+                              char *buf = g_strdup_printf ("f%s:", yyvsp[-3].s_value);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            ;}
+    break;
+
+  case 14:
+#line 124 "parser.y"
+    { GENERATE (">"); ;}
+    break;
+
+  case 15:
+#line 126 "parser.y"
+    { GENERATE ("<"); ;}
+    break;
+
+  case 16:
+#line 129 "parser.y"
+    { GENERATE ("+"); ;}
+    break;
+
+  case 17:
+#line 131 "parser.y"
+    { GENERATE ("-"); ;}
+    break;
+
+  case 18:
+#line 133 "parser.y"
+    { GENERATE ("*"); ;}
+    break;
+
+  case 19:
+#line 135 "parser.y"
+    { GENERATE ("/"); ;}
+    break;
+
+  case 20:
+#line 137 "parser.y"
+    { GENERATE ("n"); ;}
+    break;
+
+  case 21:
+#line 139 "parser.y"
+    { GENERATE ("^"); ;}
+    break;
+
+  case 22:
+#line 141 "parser.y"
+    { ;}
+    break;
+
+
+    }
+
+/* Line 1010 of yacc.c.  */
+#line 1140 "parser.c"
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+
+
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (YYPACT_NINF < yyn && yyn < YYLAST)
+	{
+	  YYSIZE_T yysize = 0;
+	  int yytype = YYTRANSLATE (yychar);
+	  const char* yyprefix;
+	  char *yymsg;
+	  int yyx;
+
+	  /* Start YYX at -YYN if negative to avoid negative indexes in
+	     YYCHECK.  */
+	  int yyxbegin = yyn < 0 ? -yyn : 0;
+
+	  /* Stay within bounds of both yycheck and yytname.  */
+	  int yychecklim = YYLAST - yyn;
+	  int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+	  int yycount = 0;
+
+	  yyprefix = ", expecting ";
+	  for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	      {
+		yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+		yycount += 1;
+		if (yycount == 5)
+		  {
+		    yysize = 0;
+		    break;
+		  }
+	      }
+	  yysize += (sizeof ("syntax error, unexpected ")
+		     + yystrlen (yytname[yytype]));
+	  yymsg = (char *) YYSTACK_ALLOC (yysize);
+	  if (yymsg != 0)
+	    {
+	      char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+	      yyp = yystpcpy (yyp, yytname[yytype]);
+
+	      if (yycount < 5)
+		{
+		  yyprefix = ", expecting ";
+		  for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+		    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+		      {
+			yyp = yystpcpy (yyp, yyprefix);
+			yyp = yystpcpy (yyp, yytname[yyx]);
+			yyprefix = " or ";
+		      }
+		}
+	      yyerror (yymsg);
+	      YYSTACK_FREE (yymsg);
+	    }
+	  else
+	    yyerror ("syntax error; also virtual memory exhausted");
+	}
+      else
+#endif /* YYERROR_VERBOSE */
+	yyerror ("syntax error");
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* If at end of input, pop the error token,
+	     then the rest of the stack, then return failure.  */
+	  if (yychar == YYEOF)
+	     for (;;)
+	       {
+		 YYPOPSTACK;
+		 if (yyssp == yyss)
+		   YYABORT;
+		 YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+		 yydestruct (yystos[*yyssp], yyvsp);
+	       }
+        }
+      else
+	{
+	  YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+	  yydestruct (yytoken, &yylval);
+	  yychar = YYEMPTY;
+
+	}
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+  /* Pacify GCC when the user code never invokes YYERROR and the label
+     yyerrorlab therefore never appears in user code.  */
+  if (0)
+     goto yyerrorlab;
+#endif
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+      YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+      yydestruct (yystos[yystate], yyvsp);
+      YYPOPSTACK;
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  YYDPRINTF ((stderr, "Shifting error token, "));
+
+  *++yyvsp = yylval;
+
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here.  |
+`----------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+#line 144 "parser.y"
+
+/* End of grammar */
+
+/* Called by yyparse on error. */
+int yyerror (char *s) {
+  /* Ignore errors, just print a warning. */
+  g_warning ("%s\n", s);
+  return 0;
+}
+
+int yylex (YYSTYPE *yylval, void *yyparam) {
+  int c;
+  parser_control *pc = (parser_control *) yyparam;
+  
+  /* Ignore whitespace, get first nonwhite character. */
+  while ((c = vfs_getc (pc->input)) == ' ' || c == '\t' || c == '\n');
+  
+  /* End of input ? */
+  if (c == EOF)
+    return 0;
+
+  /* Char starts a number => parse the number. */
+  if (isdigit (c)) {
+    vfs_fseek (pc->input, -1, SEEK_CUR); /* Put the char back. */
+    {
+      char *old_locale, *saved_locale;
+
+      old_locale = setlocale (LC_ALL, NULL);
+      saved_locale = g_strdup (old_locale);
+      setlocale (LC_ALL, "C");
+      sscanf (((VFSBuffer *)(pc->input->handle))->iter, "%lf", &yylval->d_value);
+
+      while (isdigit(c) || c == '.')
+      {
+        c = vfs_getc(pc->input);
+      }
+
+      vfs_fseek(pc->input, -1, SEEK_CUR);
+
+      setlocale (LC_ALL, saved_locale);
+      g_free (saved_locale);
+    }
+    return NUMBER;
+  }
+     
+  /* Char starts an identifier => read the name. */
+  if (isalpha (c)) {
+    GString *sym_name;
+
+    sym_name = g_string_new (NULL);
+    
+    do {
+      sym_name = g_string_append_c (sym_name, c);
+
+      /* Get another character. */
+      c = vfs_getc (pc->input);
+    } while (c != EOF && isalnum (c));
+    
+    vfs_fseek (pc->input, -1, SEEK_CUR);
+
+    yylval->s_value = sym_name->str;
+    
+    g_string_free (sym_name, FALSE);
+    
+    return NAME;
+  }
+
+  /* Any other character is a token by itself. */
+  return c;
+}
+
+static int load_name (char *str, char **name) {
+  int count = 0;
+  GString *new = g_string_new (NULL);
+
+  while (*str != 0 && *str != ':') {
+    g_string_append_c (new, *str++);
+    count++;
+  }
+
+  *name = new->str;
+  g_string_free (new, FALSE);
+
+  return count;
+}
+
+static gboolean expr_add_compile (expression_t *expr, symbol_dict_t *dict, 
+				  char *str) {
+  char op;
+  double dval;
+  int i;
+  char *name;
+
+  while ((op = *str++)) {
+    switch (op) {
+    case 'c':			/* A constant. */
+      store_byte (expr, 'c');
+      sscanf (str, "%lf%n", &dval, &i);
+      str += i;
+      store_double (expr, dval);
+      str++;			/* Skip ';' */
+      break;
+
+    case 'f':			/* A function call. */
+      store_byte (expr, 'f');
+      str += load_name (str, &name);
+      i = function_lookup (name);
+      if (i < 0) return FALSE;	/* Fail on error. */
+      store_int (expr, i);
+      g_free (name);
+      str++;			/* Skip ';' */
+      break;
+
+    case 'l':			/* Load a variable. */
+    case 's':			/* Store a variable. */
+      store_byte (expr, op);
+      str += load_name (str, &name);
+      i = dict_lookup (dict, name);
+      store_int (expr, i);
+      g_free (name);
+      str++;			/* Skip ';' */
+      break;
+
+    default:			/* Copy verbatim. */
+      store_byte (expr, op);
+      break;
+    }
+  }
+
+  return TRUE;
+}
+     
+expression_t *expr_compile_string (const char* str, symbol_dict_t *dict)
+{
+  parser_control pc;
+  VFSFile *stream;
+
+  g_return_val_if_fail(str != NULL && dict != NULL, NULL);
+
+  stream = vfs_buffer_new_from_string ( (char *) str );
+
+  pc.input = stream;
+  pc.expr = expr_new ();
+  pc.dict = dict;
+
+  if (yyparse (&pc) != 0) {
+    /* Check for error. */
+    expr_free (pc.expr);
+    pc.expr = NULL;
+  }
+
+  vfs_fclose (stream);
+
+  return pc.expr;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/parser.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,42 @@
+/* parser.h -- header file for libexp
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+
+#ifndef Included_PARSER_H
+#define Included_PARSER_H
+
+#include <glib.h>
+#include <stdio.h>
+#include <audacious/vfs.h>
+#include <audacious/vfs_buffer.h>
+
+#include "execute.h"
+
+/* Structure passed do yyparse. */
+typedef struct {
+  VFSFile *input;
+  expression_t *expr;
+  symbol_dict_t *dict;
+} parser_control;
+
+/* Prototypes. */
+expression_t *expr_compile_string (const char *str, symbol_dict_t *dict);
+
+#endif /* Included_PARSER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/parser.y	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,298 @@
+/* parser.y -- Bison parser for libexp
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+
+/* suppress conflict warnings */
+%expect 37
+
+/* C declarations. */
+%{
+#include <ctype.h>
+#include <glib.h>
+#include <locale.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "dict.h"
+#include "execute.h"
+#include "function.h"
+#include "parser.h"
+#include "storage.h"
+
+#define YYPARSE_PARAM yyparam
+#define YYLEX_PARAM yyparam
+
+static gboolean expr_add_compile (expression_t *expr, symbol_dict_t *dict,
+				  char *str);
+
+#define GENERATE(str) if (!expr_add_compile (((parser_control *)yyparam)->expr, \
+				        ((parser_control *)yyparam)->dict, str)) \
+                   YYABORT;
+%}
+
+%pure_parser
+     
+/* Data types. */
+%union {
+char *s_value;
+char c_value;
+double d_value;
+int i_value;
+}
+
+/* Terminal symbols. */     
+%token <s_value> NAME
+%token <d_value> NUMBER
+
+/* Precedence rules. */     
+%right '='
+%left '-' '+'
+%left '*' '/'
+%left NEG
+%right '^'
+     
+/* Grammar follows */
+%%
+
+/* Input consits of a (possibly empty) list of expressions. */
+input:			/* empty */
+			| input expression_list
+;
+
+/* expression_list is a ';' separated list of expressions. */
+expression_list:	/* empty */
+    			| expression
+			    { } 
+                        | expression_list ';'
+    			| error ';'	
+			    { yyerrok; }
+
+/* argument list is a comma separated list od expressions */
+argument_list:
+			expression
+                            {
+                            }
+                        | argument_list ',' expression
+                            {
+                            }
+     
+/* expression is a C-like expression. */
+expression:		NUMBER
+			    { 
+                              char *buf = g_strdup_printf ("c%f:", $1);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            }
+		        | NAME
+			    { 
+                              char *buf = g_strdup_printf ("l%s:", $1);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            }
+    			| NAME '=' expression
+			    { 
+                              char *buf = g_strdup_printf ("s%s:", $1);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            }
+                        | NAME '(' argument_list ')'
+                            {
+                              char *buf = g_strdup_printf ("f%s:", $1);
+                              GENERATE (buf); 
+                              g_free (buf);
+                            }
+
+			| expression '>' expression
+			    { GENERATE (">"); }
+			| expression '<' expression
+			    { GENERATE ("<"); }
+
+			| expression '+' expression
+			    { GENERATE ("+"); }
+			| expression '-' expression
+			    { GENERATE ("-"); }
+			| expression '*' expression
+			    { GENERATE ("*"); }
+			| expression '/' expression
+			    { GENERATE ("/"); }
+			| '-' expression %prec NEG
+			    { GENERATE ("n"); }
+			| expression '^' expression
+			    { GENERATE ("^"); }
+			| '(' expression ')'
+			    { }
+;
+
+%%
+/* End of grammar */
+
+/* Called by yyparse on error. */
+int yyerror (char *s) {
+  /* Ignore errors, just print a warning. */
+  g_warning ("%s\n", s);
+  return 0;
+}
+
+int yylex (YYSTYPE *yylval, void *yyparam) {
+  int c;
+  parser_control *pc = (parser_control *) yyparam;
+  
+  /* Ignore whitespace, get first nonwhite character. */
+  while ((c = vfs_getc (pc->input)) == ' ' || c == '\t' || c == '\n');
+  
+  /* End of input ? */
+  if (c == EOF)
+    return 0;
+
+  /* Char starts a number => parse the number. */
+  if (isdigit (c)) {
+    vfs_fseek (pc->input, -1, SEEK_CUR); /* Put the char back. */
+    {
+      char *old_locale, *saved_locale;
+
+      old_locale = setlocale (LC_ALL, NULL);
+      saved_locale = g_strdup (old_locale);
+      setlocale (LC_ALL, "C");
+      sscanf (((VFSBuffer *)(pc->input->handle))->iter, "%lf", &yylval->d_value);
+
+      while (isdigit(c) || c == '.')
+      {
+        c = vfs_getc(pc->input);
+      }
+
+      vfs_fseek(pc->input, -1, SEEK_CUR);
+
+      setlocale (LC_ALL, saved_locale);
+      g_free (saved_locale);
+    }
+    return NUMBER;
+  }
+     
+  /* Char starts an identifier => read the name. */
+  if (isalpha (c)) {
+    GString *sym_name;
+
+    sym_name = g_string_new (NULL);
+    
+    do {
+      sym_name = g_string_append_c (sym_name, c);
+
+      /* Get another character. */
+      c = vfs_getc (pc->input);
+    } while (c != EOF && isalnum (c));
+    
+    vfs_fseek (pc->input, -1, SEEK_CUR);
+
+    yylval->s_value = sym_name->str;
+    
+    g_string_free (sym_name, FALSE);
+    
+    return NAME;
+  }
+
+  /* Any other character is a token by itself. */
+  return c;
+}
+
+static int load_name (char *str, char **name) {
+  int count = 0;
+  GString *new = g_string_new (NULL);
+
+  while (*str != 0 && *str != ':') {
+    g_string_append_c (new, *str++);
+    count++;
+  }
+
+  *name = new->str;
+  g_string_free (new, FALSE);
+
+  return count;
+}
+
+static gboolean expr_add_compile (expression_t *expr, symbol_dict_t *dict, 
+				  char *str) {
+  char op;
+  double dval;
+  int i;
+  char *name;
+
+  while ((op = *str++)) {
+    switch (op) {
+    case 'c':			/* A constant. */
+      store_byte (expr, 'c');
+      sscanf (str, "%lf%n", &dval, &i);
+      str += i;
+      store_double (expr, dval);
+      str++;			/* Skip ';' */
+      break;
+
+    case 'f':			/* A function call. */
+      store_byte (expr, 'f');
+      str += load_name (str, &name);
+      i = function_lookup (name);
+      if (i < 0) return FALSE;	/* Fail on error. */
+      store_int (expr, i);
+      g_free (name);
+      str++;			/* Skip ';' */
+      break;
+
+    case 'l':			/* Load a variable. */
+    case 's':			/* Store a variable. */
+      store_byte (expr, op);
+      str += load_name (str, &name);
+      i = dict_lookup (dict, name);
+      store_int (expr, i);
+      g_free (name);
+      str++;			/* Skip ';' */
+      break;
+
+    default:			/* Copy verbatim. */
+      store_byte (expr, op);
+      break;
+    }
+  }
+
+  return TRUE;
+}
+     
+expression_t *expr_compile_string (const char* str, symbol_dict_t *dict)
+{
+  parser_control pc;
+  VFSFile *stream;
+
+  g_return_val_if_fail(str != NULL && dict != NULL, NULL);
+
+  stream = vfs_buffer_new_from_string ( (char *) str );
+
+  pc.input = stream;
+  pc.expr = expr_new ();
+  pc.dict = dict;
+
+  if (yyparse (&pc) != 0) {
+    /* Check for error. */
+    expr_free (pc.expr);
+    pc.expr = NULL;
+  }
+
+  vfs_fclose (stream);
+
+  return pc.expr;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/storage.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,79 @@
+/* storage.c --
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+ 
+#include <glib.h>
+#include <stddef.h>
+ 
+#include "storage.h"
+
+/* Code block. */
+
+expression_t *expr_new (void) {
+  expression_t *new_expr;
+
+  new_expr = (expression_t *)g_malloc (sizeof(expression_t));
+  new_expr->data = g_string_new (NULL);
+  return new_expr;
+}
+
+void expr_free (expression_t *expr) {
+  if (!expr)
+    return;
+
+  g_string_free (expr->data, TRUE);
+  g_free (expr);
+}
+
+static void load_data (char *str, void *dest, size_t size) {
+  char *ch = (char *)dest;
+  while (size--)
+    *ch++ = *str++;
+}
+
+int load_int (char *str) {
+  int val;
+  load_data (str, &val, sizeof(val));
+  return val;
+}
+
+double load_double (char *str) {
+  double val;
+  load_data (str, &val, sizeof(val));
+  return val;
+}
+
+static void store_data (expression_t *expr, void *src, size_t size) {
+  char *ch = (char *)src;
+  while (size--)
+    store_byte (expr, *ch++);
+}
+
+void store_byte (expression_t *expr, char byte) {
+  g_string_append_c (expr->data, byte);
+}
+
+void store_int (expression_t *expr, int val) {
+  store_data (expr, &val, sizeof(val));
+}
+
+void store_double (expression_t *expr, double val) {
+  store_data (expr, &val, sizeof(val));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/libcalc/storage.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,42 @@
+/* storage.h -- 
+ *
+ * Copyright (C) 2001 Janusz Gregorczyk <jgregor@kki.net.pl>
+ *
+ * This file is part of xvs.
+ *
+ * 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. 
+ */
+
+#ifndef Included_STORAGE_H
+#define Included_STORAGE_H
+
+#include <glib.h>
+
+typedef struct {
+  GString *data;
+} expression_t;
+
+/* Expr block. */
+expression_t *expr_new (void);
+void expr_free (expression_t *expr);
+
+int load_int (char *str);
+double load_double (char *str);
+
+void store_byte (expression_t *expr, char byte);
+void store_int (expression_t *expr, int val);
+void store_double (expression_t *expr, double val);
+
+#endif /* Included_STORAGE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/misc.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,128 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+#include "pn_utils.h"
+
+/* ******************** misc_floater ******************** */
+static struct pn_actuator_option_desc misc_floater_opts[] =
+{
+  { "value", "The colour value for the floater.",
+    OPT_TYPE_INT, { ival: 255 } },
+  { NULL }
+};
+
+typedef enum
+{
+  float_up    = 0x1,
+  float_down  = 0x2,
+  float_left  = 0x4,
+  float_right = 0x8,
+} FloaterDirection;
+
+struct floater_state_data
+{
+  FloaterDirection dir;
+  gint x;
+  gint y;
+};
+
+static void
+misc_floater_init(gpointer *data)
+{
+  struct floater_state_data *opaque_data = g_new0(struct floater_state_data, 1);
+  *data = opaque_data;
+  opaque_data->x = rand() % pn_image_data->width;
+  opaque_data->y = rand() % pn_image_data->height;
+  opaque_data->dir = (FloaterDirection) rand() % 15; /* sum of all dir values */
+}
+
+static void
+misc_floater_cleanup(gpointer data)
+{
+  g_free(data);
+}
+
+/*
+ * This implementation isn't very great.
+ * Anyone want to improve it? :(
+ */
+static void
+misc_floater_exec(const struct pn_actuator_option *opts, gpointer data)
+{
+  struct floater_state_data *opaque_data = (struct floater_state_data *) data;
+  guchar value = (opts[0].val.ival < 0 || opts[0].val.ival > 255) ? 255 : opts[0].val.ival;
+
+  /* determine the root coordinate first */
+  if (opaque_data->dir & float_up)
+    opaque_data->y -= 1;
+  if (opaque_data->dir & float_down)
+    opaque_data->y += 1;
+
+  if (opaque_data->dir & float_left)
+    opaque_data->x -= 1;
+  if (opaque_data->dir & float_right)
+    opaque_data->x += 1;
+
+  /* make sure we're within surface boundaries. segfaults suck, afterall. */
+  if (opaque_data->x + 1 <= pn_image_data->width &&
+       opaque_data->x - 1 >= 0 &&
+       opaque_data->y + 1 <= pn_image_data->height &&
+       opaque_data->y - 1 >= 0)
+    {
+      /* draw it. i could use a loop here, but i don't see much reason in it,
+       * so i don't think i will at this time.  -nenolod
+       */
+      pn_image_data->surface[0][PN_IMG_INDEX(opaque_data->x, opaque_data->y)]     = value;
+      pn_image_data->surface[0][PN_IMG_INDEX(opaque_data->x + 1, opaque_data->y)] = value;
+      pn_image_data->surface[0][PN_IMG_INDEX(opaque_data->x - 1, opaque_data->y)] = value;
+      pn_image_data->surface[0][PN_IMG_INDEX(opaque_data->x, opaque_data->y + 1)] = value;
+      pn_image_data->surface[0][PN_IMG_INDEX(opaque_data->x, opaque_data->y - 1)] = value;
+    }
+
+  /* check if we need to change direction yet, and if so, do so. */
+  if (pn_new_beat == TRUE)
+    opaque_data->dir = (FloaterDirection) rand() % 15; /* sum of all dir values */
+
+  /* now adjust the direction so we stay in boundary */
+  if (opaque_data->x - 1 <= 0 && opaque_data->dir & float_left)
+    {
+      opaque_data->dir &= ~float_left;
+      opaque_data->dir |= float_right;
+    }
+
+  if (opaque_data->x + 1 >= pn_image_data->width && opaque_data->dir & float_right)
+    {
+      opaque_data->dir &= ~float_right;
+      opaque_data->dir |= float_left;
+    }
+
+  if (opaque_data->y - 1 <= 0 && opaque_data->dir & float_up)
+    {
+      opaque_data->dir &= ~float_up;
+      opaque_data->dir |= float_down;
+    }
+
+  if (opaque_data->y + 1 >= pn_image_data->height && opaque_data->dir & float_down)
+    {
+      opaque_data->dir &= ~float_down;
+      opaque_data->dir |= float_up;
+    }
+}
+
+struct pn_actuator_desc builtin_misc_floater =
+{
+  "misc_floater",
+  "Floating Particle",
+  "A floating particle.",
+  0, misc_floater_opts,
+  misc_floater_init, misc_floater_cleanup, misc_floater_exec
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/paranormal.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,234 @@
+/* FIXME: add fullscreen / screenshots */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <SDL.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+
+/* SDL stuffs */
+SDL_Surface *screen;
+
+/* Globals */
+struct pn_rc         *pn_rc;
+struct pn_image_data *pn_image_data;
+struct pn_sound_data *pn_sound_data;
+
+/* Trig Pre-Computes */
+float sin_val[360];
+float cos_val[360];
+
+gboolean pn_new_beat;
+
+extern SDL_mutex *config_mutex;
+
+/* **************** drawing doodads **************** */
+
+static void
+blit_to_screen (void)
+{
+  int j;
+
+  SDL_LockSurface (screen);
+
+  /* FIXME: add scaling support */
+
+  SDL_SetPalette (screen, SDL_LOGPAL|SDL_PHYSPAL,
+		  (SDL_Color*)pn_image_data->cmap, 0, 256);
+  SDL_SetAlpha (screen, 0, 255);
+
+  for (j=0; j<pn_image_data->height; j++)
+      memcpy (screen->pixels + j*screen->pitch,
+	      pn_image_data->surface[0] + j*pn_image_data->width,
+	      pn_image_data->width);
+
+
+  SDL_UnlockSurface (screen);
+
+  SDL_UpdateRect (screen, 0, 0, 0, 0);
+}
+
+static void
+resize_video (guint w, guint h)
+{
+  pn_image_data->width = w;
+  pn_image_data->height = h;
+
+  if (pn_image_data->surface[0])
+    g_free (pn_image_data->surface[0]);
+  if (pn_image_data->surface[1])
+    g_free (pn_image_data->surface[1]);
+
+  pn_image_data->surface[0] = g_malloc0 (w * h);
+  pn_image_data->surface[1] = g_malloc0 (w * h);
+
+  screen = SDL_SetVideoMode (w, h, 8, SDL_HWSURFACE |
+			     SDL_HWPALETTE | SDL_RESIZABLE);
+  if (! screen)
+    pn_fatal_error ("Unable to create a new SDL window: %s",
+		    SDL_GetError ());
+}
+
+static void
+take_screenshot (void)
+{
+  char fname[32];
+  struct stat buf;
+  int i=0;
+
+  do
+    sprintf (fname, "pn_%05d.bmp", ++i);
+  while (stat (fname, &buf) == 0);
+
+  SDL_SaveBMP (screen, fname);
+}
+
+/* FIXME: This should resize the video to a user-set
+   fullscreen res */
+static void
+toggle_fullscreen (void)
+{
+  SDL_WM_ToggleFullScreen (screen);  
+  if (SDL_ShowCursor (SDL_QUERY) == SDL_ENABLE)
+    SDL_ShowCursor (SDL_DISABLE);
+  else
+    SDL_ShowCursor (SDL_ENABLE);
+}
+
+/* **************** basic renderer management **************** */
+void
+pn_init (void)
+{
+  int i;
+#ifdef FULLSCREEN_HACK
+  char SDL_windowhack[32];
+  GdkScreen *screen;
+#endif
+
+  pn_sound_data = g_new0 (struct pn_sound_data, 1);
+  pn_image_data = g_new0 (struct pn_image_data, 1);
+
+#ifdef FULLSCREEN_HACK
+  screen = gdk_screen_get_default();
+  sprintf(SDL_windowhack,"SDL_WINDOWID=%d",
+          GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(screen)));
+          putenv(SDL_windowhack);
+#endif
+
+  if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) < 0)
+    pn_fatal_error ("Unable to initialize SDL: %s", SDL_GetError ());
+
+#ifndef FULLSCREEN_HACK
+  resize_video (640, 360);
+#else
+  resize_video (1280, 1024);
+#endif
+
+  SDL_WM_SetCaption ("Rovascope " VERSION, PACKAGE);
+
+  for(i=0; i<360; i++)
+    {
+      sin_val[i] = sin(i*(M_PI/180.0));
+      cos_val[i] = cos(i*(M_PI/180.0));
+    }
+}
+
+void
+pn_cleanup (void)
+{
+  SDL_FreeSurface (screen);
+  SDL_Quit ();
+
+
+  if (pn_image_data)
+    {
+      if (pn_image_data->surface[0])
+	g_free (pn_image_data->surface[0]);
+      if (pn_image_data->surface[1])
+	g_free (pn_image_data->surface[1]);
+      g_free (pn_image_data);
+    }
+  if (pn_sound_data)
+    g_free (pn_sound_data);
+}
+
+/* Renders one frame and handles the SDL window */
+void
+pn_render (void)
+{
+  SDL_Event event;
+
+  /* Handle window events */
+  while (SDL_PollEvent (&event))
+    {
+      switch (event.type)
+	{
+	case SDL_QUIT:
+	  pn_quit ();
+	  g_assert_not_reached ();
+	case SDL_KEYDOWN:
+	  switch (event.key.keysym.sym)
+	    {
+	    case SDLK_ESCAPE:
+	      pn_quit ();
+	      g_assert_not_reached ();
+	    case SDLK_RETURN:
+	      if (event.key.keysym.mod & (KMOD_ALT | KMOD_META))
+		toggle_fullscreen ();
+	      break;
+	    case SDLK_BACKQUOTE:
+	      take_screenshot ();
+	      break;
+	    default:
+              break;
+	    }
+	  break;
+	case SDL_VIDEORESIZE:
+	  resize_video (event.resize.w, event.resize.h);	  
+	  break;
+	}
+    }
+
+  pn_new_beat = pn_is_new_beat();
+
+  if (pn_rc->actuator)
+    {
+      exec_actuator (pn_rc->actuator);
+      blit_to_screen ();
+    }
+
+  if (pn_new_beat && (rand() % 4 == 0))
+    {
+      struct pn_actuator *a;
+      GSList *list = *(GSList **)pn_rc->actuator->data;
+
+      container_remove_actuator (pn_rc->actuator, list->data);
+
+      SDL_mutexP(config_mutex);
+      a = rovascope_get_random_actuator();
+      container_add_actuator (pn_rc->actuator, a);
+      SDL_mutexV(config_mutex);
+    }
+}
+
+/* this MUST be called if a builtin's output is to surface[1]
+   (by the builtin, after it is done) */
+void
+pn_swap_surfaces (void)
+{
+  guchar *tmp = pn_image_data->surface[0];
+  pn_image_data->surface[0] = pn_image_data->surface[1];
+  pn_image_data->surface[1] = tmp;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/paranormal.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,64 @@
+#ifndef _PARANORMAL_H
+#define _PARANORMAL_H
+
+#include <glib.h>
+
+#include "actuators.h"
+
+struct pn_sound_data
+{
+  gint16 pcm_data[2][512];
+  gint16 freq_data[2][256];
+};
+
+struct pn_image_data
+{
+  int width, height;
+  struct pn_color cmap[256];
+  guchar *surface[2];
+};
+
+/* The executable (ie xmms.c or standalone.c)
+   is responsible for allocating this and filling
+   it with default/saved values */
+struct pn_rc
+{
+  struct pn_actuator *actuator;
+};
+
+/* core funcs */
+void pn_init (void);
+void pn_cleanup (void);
+void pn_render (void);
+void pn_swap_surfaces (void);
+
+/* Implemented elsewhere (ie xmms.c or standalone.c) */
+void pn_set_rc ();
+void pn_fatal_error (const char *fmt, ...);
+void pn_error (const char *fmt, ...);
+void pn_quit (void);
+
+/* Implimented in cfg.c */
+void pn_configure (void);
+
+/* globals used for rendering */
+extern struct pn_rc         *pn_rc;
+extern struct pn_sound_data *pn_sound_data;
+extern struct pn_image_data *pn_image_data;
+
+extern gboolean pn_new_beat;
+
+/* global trig pre-computes */
+extern float sin_val[360];
+extern float cos_val[360];
+
+/* beat detection */
+int pn_is_new_beat(void);
+
+struct pn_actuator *rovascope_get_random_colourmap(void);
+struct pn_actuator *rovascope_get_random_general(void);
+struct pn_actuator *rovascope_get_random_normal_scope(void);
+
+struct pn_actuator *rovascope_get_random_actuator(void);
+
+#endif /* _PARANORMAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/plugin.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,468 @@
+/* FIXME: issues with not uniniting variables between
+   enables?  I wasn't too careful about that, but it
+   seems to work fine.  If there are problems perhaps
+   look for a bug there?
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <memory.h>
+#include <math.h>
+#include <setjmp.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+#include <audacious/plugin.h>
+#include <SDL/SDL.h>
+#include <SDL/SDL_thread.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+#include "containers.h"
+
+/* Error reporting dlg */
+static GtkWidget *err_dialog;
+
+/* Draw thread stuff */
+/* FIXME: Do I need mutex for pn_done? */
+static SDL_Thread *draw_thread = NULL;
+static SDL_mutex *sound_data_mutex;
+SDL_mutex *config_mutex;
+
+static gboolean pn_done = FALSE;
+jmp_buf quit_jmp;
+gboolean timeout_set = FALSE;
+guint quit_timeout;
+
+/* Sound stuffs */
+static gboolean new_pcm_data = FALSE;
+static gboolean new_freq_data = FALSE;
+static gint16 tmp_pcm_data[2][512];
+static gint16 tmp_freq_data[2][256];
+
+/* XMMS interface */
+static void pn_xmms_init (void);
+static void pn_xmms_cleanup (void);
+static void pn_xmms_about (void);
+static void pn_xmms_configure (void);
+static void pn_xmms_render_pcm (gint16 data[2][512]);
+static void pn_xmms_render_freq (gint16 data[2][256]);
+
+static VisPlugin pn_vp = 
+{
+  NULL,
+  NULL,
+  0,
+  "Rovascope " VERSION,
+  2,
+  2,
+  pn_xmms_init,
+  pn_xmms_cleanup,
+  pn_xmms_about,
+  pn_xmms_configure,
+  NULL, /* disable_plugin */
+  NULL, /* pn_xmms_playback_start */
+  NULL, /* pn_xmms_playback_stop */
+  pn_xmms_render_pcm,
+  pn_xmms_render_freq
+};
+
+VisPlugin *
+get_vplugin_info (void)
+{
+  return &pn_vp;
+}
+
+static void
+load_pn_rc (void)
+{
+  struct pn_actuator *a, *b;
+
+  if (! pn_rc)
+      pn_rc = g_new0 (struct pn_rc, 1);
+
+  /* load a default preset */
+  pn_rc->actuator = create_actuator ("container_simple");
+  if (! pn_rc->actuator) goto ugh;
+  a = create_actuator ("container_once");
+  if (! a) goto ugh;
+
+  b = rovascope_get_random_colourmap ();
+
+  container_add_actuator (a, b);
+  container_add_actuator (pn_rc->actuator, a);
+
+  a = rovascope_get_random_normal_scope ();
+  if (! a) goto ugh;
+  container_add_actuator (pn_rc->actuator, a);
+
+  a = create_actuator ("xform_movement");
+  if (! a) goto ugh;
+  a->options[0].val.sval = g_strdup("d = cos(d)^2;");
+  container_add_actuator (pn_rc->actuator, a);
+
+  a = rovascope_get_random_general();
+  if (! a) goto ugh;
+  container_add_actuator (pn_rc->actuator, a);
+
+  a = rovascope_get_random_general();
+  if (! a) goto ugh;
+  container_add_actuator (pn_rc->actuator, a);
+
+  return;
+
+ ugh:
+  if (pn_rc->actuator)
+    destroy_actuator (pn_rc->actuator);
+  pn_error ("Error loading default preset");
+}
+
+static int
+draw_thread_fn (gpointer data)
+{
+  gfloat fps = 0.0;
+  guint last_time = 0, last_second = 0;
+  guint this_time;
+  pn_init ();
+
+  /* Used when pn_quit is called from this thread */
+  if (setjmp (quit_jmp) != 0)
+    pn_done = TRUE;
+
+  while (! pn_done)
+    {
+      SDL_mutexP (sound_data_mutex);
+      if (new_freq_data)
+	{
+	  memcpy (pn_sound_data->freq_data, tmp_freq_data,
+		  sizeof (gint16) * 2 * 256);
+	  new_freq_data = FALSE;
+	}
+      if (new_pcm_data)
+	{
+	  memcpy (pn_sound_data->pcm_data, tmp_pcm_data,
+		  sizeof (gint16) * 2 * 512);
+	  new_freq_data = FALSE;
+	}
+      SDL_mutexV (sound_data_mutex);
+      SDL_mutexP (config_mutex);
+      pn_render ();
+      SDL_mutexV (config_mutex);
+
+      /* Compute the FPS */
+      this_time = SDL_GetTicks ();
+
+      fps = fps * .95 + (1000. / (gfloat) (this_time - last_time)) * .05;
+      if (this_time > 2000 + last_second)
+        {
+          last_second = this_time;
+          g_print ("FPS: %f\n", fps);
+        }
+      last_time = this_time;
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+      sched_yield();
+#endif
+    }
+
+  /* Just in case a pn_quit () was called in the loop */
+/*    SDL_mutexV (sound_data_mutex); */
+
+  pn_cleanup ();
+
+  return 0;
+}
+
+/* Is there a better way to do this? this = messy
+   It appears that calling disable_plugin () in some
+   thread other than the one that called pn_xmms_init ()
+   causes a seg fault :( */
+static int
+quit_timeout_fn (gpointer data)
+{
+  if (pn_done)
+    {
+      pn_vp.disable_plugin (&pn_vp);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+pn_xmms_init (void)
+{
+  /* If it isn't already loaded, load the run control */
+  if (! pn_rc)
+	  load_pn_rc ();
+
+  sound_data_mutex = SDL_CreateMutex ();
+  config_mutex = SDL_CreateMutex ();
+  if (! sound_data_mutex)
+    pn_fatal_error ("Unable to create a new mutex: %s",
+		    SDL_GetError ());
+
+  pn_done = FALSE;
+  draw_thread = SDL_CreateThread (draw_thread_fn, NULL);
+  if (! draw_thread)
+    pn_fatal_error ("Unable to create a new thread: %s",
+		    SDL_GetError ());
+
+  /* Add a gtk timeout to test for quits */
+  quit_timeout = gtk_timeout_add (1000, quit_timeout_fn, NULL);
+  timeout_set = TRUE;
+}
+
+static void
+pn_xmms_cleanup (void)
+{
+  if (timeout_set)
+    {
+      gtk_timeout_remove (quit_timeout);
+      timeout_set = FALSE;
+    }
+
+  if (draw_thread)
+    {
+      pn_done = TRUE;
+      SDL_WaitThread (draw_thread, NULL);
+      draw_thread = NULL;
+    }
+
+  if (sound_data_mutex)
+    {
+      SDL_DestroyMutex (sound_data_mutex);
+      sound_data_mutex = NULL;
+    }
+
+  if (config_mutex)
+    {
+      SDL_DestroyMutex (config_mutex);
+      config_mutex = NULL;
+    }
+}
+
+static void
+about_close_clicked(GtkWidget *w, GtkWidget **window)
+{
+	gtk_widget_destroy(*window);
+	*window=NULL;
+}
+
+static void
+about_closed(GtkWidget *w, GdkEvent *e, GtkWidget **window)
+{
+	about_close_clicked(w,window);
+}
+
+static void
+pn_xmms_about (void)
+{
+  xmms_show_message("About Rovascope", 
+
+"Rovascope " VERSION "\n\n\
+Copyright (C) 2007, William Pitcock <nenolod -at- dereferenced.org>\n\
+\
+Derived from:\
+Paranormal Visualization Studio\n\
+\n\
+Copyright (C) 2006, William Pitcock <nenolod -at- nenolod.net>\n\
+Portions Copyright (C) 2001, Jamie Gennis <jgennis -at- mindspring.com>\n\
+\n\
+This program is free software; you can redistribute it and/or modify\n\
+it under the terms of the GNU General Public License as published by\n\
+the Free Software Foundation; either version 2 of the License, or\n\
+(at your option) any later version.\n\
+\n\
+This program is distributed in the hope that it will be useful,\n\
+but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
+GNU General Public License for more details.\n\
+\n\
+You should have received a copy of the GNU General Public License\n\
+along with this program; if not, write to the Free Software\n\
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307\n\
+USA", _("Ok"), FALSE, NULL, NULL);
+}
+
+static void
+pn_xmms_configure (void)
+{
+
+}
+
+static void
+pn_xmms_render_pcm (gint16 data[2][512])
+{
+  SDL_mutexP (sound_data_mutex);
+  memcpy (tmp_pcm_data, data, sizeof (gint16) * 2 * 512);
+  new_pcm_data = TRUE;
+  SDL_mutexV (sound_data_mutex);
+}
+
+static void
+pn_xmms_render_freq (gint16 data[2][256])
+{
+  SDL_mutexP (sound_data_mutex);
+  memcpy (tmp_freq_data, data, sizeof (gint16) * 2 * 256);
+  new_freq_data = TRUE;
+  SDL_mutexV (sound_data_mutex);
+}
+
+/* **************** paranormal.h stuff **************** */
+
+void
+pn_set_rc (struct pn_rc *new_rc)
+{
+  if (config_mutex)
+    SDL_mutexP (config_mutex);
+
+  if (! pn_rc)
+    load_pn_rc ();
+
+  if (pn_rc->actuator)
+    destroy_actuator (pn_rc->actuator);
+  pn_rc->actuator = new_rc->actuator;
+
+  if (config_mutex)
+    SDL_mutexV (config_mutex);
+}
+
+void
+pn_fatal_error (const char *fmt, ...)
+{
+  char *errstr;
+  va_list ap;
+  GtkWidget *dialog;
+  GtkWidget *close, *label;
+
+  /* Don't wanna try to lock GDK if we already have it */
+  if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
+    GDK_THREADS_ENTER ();
+
+  /* now report the error... */
+  va_start (ap, fmt);
+  errstr = g_strdup_vprintf (fmt, ap);
+  va_end (ap);
+
+  dialog=gtk_dialog_new();
+  gtk_window_set_title(GTK_WINDOW(dialog), "Error - Paranormal Visualization Studio - " VERSION);
+  gtk_container_border_width (GTK_CONTAINER (dialog), 8);
+
+  label=gtk_label_new(errstr);
+  fprintf (stderr, "%s\n", errstr);
+  g_free (errstr);
+
+  close = gtk_button_new_with_label ("Close");
+  gtk_signal_connect_object (GTK_OBJECT (close), "clicked",
+			     GTK_SIGNAL_FUNC (gtk_widget_destroy),
+			     GTK_OBJECT (dialog));
+
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, FALSE,
+		      FALSE, 0);
+  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), close,
+		      FALSE, FALSE, 0);
+  gtk_widget_show (label);
+  gtk_widget_show (close);
+
+  gtk_widget_show (dialog);
+  gtk_widget_grab_focus (dialog);
+
+  if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
+    GDK_THREADS_LEAVE ();
+
+  pn_quit ();
+}
+
+
+void
+pn_error (const char *fmt, ...)
+{
+  char *errstr;
+  va_list ap;
+  static GtkWidget *text;
+  static GtkTextBuffer *textbuf;
+
+  /* now report the error... */
+  va_start (ap, fmt);
+  errstr = g_strdup_vprintf (fmt, ap);
+  va_end (ap);
+  fprintf (stderr, "Rovascope-CRITICAL **: %s\n", errstr);
+
+  /* This is the easiest way of making sure we don't
+     get stuck trying to lock a mutex that this thread
+     already owns since this fn can be called from either
+     thread */
+  if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
+    GDK_THREADS_ENTER ();
+
+  if (! err_dialog)
+    {
+      GtkWidget *close;
+
+      err_dialog=gtk_dialog_new();
+      gtk_window_set_title (GTK_WINDOW (err_dialog), "Error - Paranormal Visualization Studio - " VERSION);
+      gtk_window_set_policy (GTK_WINDOW (err_dialog), FALSE, FALSE, FALSE);
+      gtk_widget_set_usize (err_dialog, 400, 200);
+      gtk_container_border_width (GTK_CONTAINER (err_dialog), 8);
+
+      textbuf = gtk_text_buffer_new(NULL);
+      text = gtk_text_view_new_with_buffer (textbuf);
+
+      close = gtk_button_new_with_label ("Close");
+      gtk_signal_connect_object (GTK_OBJECT (close), "clicked",
+				 GTK_SIGNAL_FUNC (gtk_widget_hide),
+				 GTK_OBJECT (err_dialog));
+      gtk_signal_connect_object (GTK_OBJECT (err_dialog), "delete-event",
+				 GTK_SIGNAL_FUNC (gtk_widget_hide),
+				 GTK_OBJECT (err_dialog));
+
+      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->vbox), text, FALSE,
+			  FALSE, 0);
+      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (err_dialog)->action_area), close,
+			  FALSE, FALSE, 0);
+      gtk_widget_show (text);
+      gtk_widget_show (close);
+    }
+
+  gtk_text_buffer_set_text(GTK_TEXT_BUFFER(textbuf), errstr, -1);
+  g_free (errstr);
+
+  gtk_widget_show (err_dialog);
+  gtk_widget_grab_focus (err_dialog);
+
+  if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
+    GDK_THREADS_LEAVE ();
+}
+
+
+/* This is confusing...
+   Don't call this from anywhere but the draw thread or
+   the initialization xmms thread (ie NOT the xmms sound
+   data functions) */
+void
+pn_quit (void)
+{
+  if (draw_thread && SDL_ThreadID () == SDL_GetThreadID (draw_thread))
+    {
+      /* We're in the draw thread so be careful */
+      longjmp (quit_jmp, 1);
+    }
+  else
+    {
+      /* We're not in the draw thread, so don't sweat it...
+	 addendum: looks like we have to bend over backwards (forwards?)
+	 for xmms here too */
+      pn_vp.disable_plugin (&pn_vp);
+      while (1)
+	gtk_main_iteration ();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/pn_utils.h	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,15 @@
+#ifndef _PN_UTILS_H
+#define _PN_UTILS_H
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+#define CAP(i,c) (i > c ? c : i < -(c) ? -(c) : i)
+#define CAPHILO(i,h,l) (i > h ? h : i < l ? l : i)
+#define CAPHI(i,h) (i > h ? h : i)
+#define CAPLO(i,l) (i < l ? l : i)
+
+#define PN_IMG_INDEX(x,y) ((x) + (pn_image_data->width * (y)))
+
+#endif /* _PN_UTILS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/wave.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,528 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <math.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+#include "pn_utils.h"
+
+#include "drawing.h"
+
+#include "libcalc/calc.h"
+
+/* **************** wave_horizontal **************** */
+struct pn_actuator_option_desc wave_horizontal_opts[] =
+{
+  {"channels", "Which sound channels to use: negative = channel 1, \npositive = channel 2, "
+   "zero = both (two wave-forms.)", OPT_TYPE_INT, {ival: -1} },
+  {"value", "The colour value to use.", OPT_TYPE_INT, {ival: 255} },
+  {"lines", "Use lines instead of dots.", OPT_TYPE_BOOLEAN, {bval: TRUE} },
+  { NULL }
+};
+
+static void
+wave_horizontal_exec_dots (const struct pn_actuator_option *opts,
+		    gpointer data)
+{
+  int i;
+  int channel = ( opts[0].val.ival < 0 ) ? 0 : 1;
+  guchar value = (opts[1].val.ival < 0 || opts[1].val.ival > 255) ? 255 : opts[1].val.ival;
+
+  for (i=0; i<pn_image_data->width; i++) {
+
+    /*single channel, centered horz.*/
+    if ( opts[0].val.ival ) {
+      pn_image_data->surface[0][PN_IMG_INDEX (i, (pn_image_data->height>>1)
+					      - CAP (pn_sound_data->pcm_data[channel]
+						     [i*512/pn_image_data->width]>>8,
+						     (pn_image_data->height>>1)-1))]
+	= value;
+    }
+
+    /*both channels, at 1/4 and 3/4 of the screen*/
+    else {
+      pn_image_data->surface[0][PN_IMG_INDEX( i, (pn_image_data->height>>2) -
+					      CAP( (pn_sound_data->pcm_data[0]
+						    [i*512/pn_image_data->width]>>9),
+						   (pn_image_data->height>>2)-1))]
+	= value;
+
+      pn_image_data->surface[0][PN_IMG_INDEX( i, 3*(pn_image_data->height>>2) -
+					      CAP( (pn_sound_data->pcm_data[1]
+						    [i*512/pn_image_data->width]>>9),
+						   (pn_image_data->height>>2)-1))]
+	= value;
+    }
+  }
+}
+
+void
+wave_horizontal_exec_lines (const struct pn_actuator_option *opts,
+		    gpointer data)
+{
+  int channel = ( opts[0].val.ival < 0 ) ? 0 : 1;
+  guchar value = (opts[1].val.ival < 0 || opts[1].val.ival > 255) ? 255 : opts[1].val.ival;
+  int *x_pos, *y_pos;	/* dynamic tables which store the positions for the line */
+  int *x2_pos, *y2_pos;	/* dynamic tables which store the positions for the line */
+  int i;
+  float step;
+
+  x_pos = g_new0(int, 257);
+  y_pos = g_new0(int, 257);
+  x2_pos = g_new0(int, 257);
+  y2_pos = g_new0(int, 257);
+
+  step = pn_image_data->width / 256.;
+
+  /* calculate the line. */
+  for (i = 0; i < 256; i++)
+    {
+      if (opts[0].val.ival != 0)
+        {
+           x_pos[i] = i * step;
+           y_pos[i] = (pn_image_data->height>>1) - 
+			CAP (pn_sound_data->pcm_data[channel][i * 2]>>8, 
+			(pn_image_data->height>>1)-1);
+        }
+      else
+        {
+           x_pos[i] = i * step;
+           y_pos[i] = (pn_image_data->height>>2) - 
+			CAP (pn_sound_data->pcm_data[0][i * 2]>>9,
+			(pn_image_data->height>>2)-1);
+
+           x2_pos[i] = i * step;
+           y2_pos[i] = 3*(pn_image_data->height>>2) - 
+			CAP (pn_sound_data->pcm_data[1][i * 2]>>9,
+			(pn_image_data->height>>2)-1);
+
+        }
+    }
+
+  /* draw the line. */
+  for (i = 1; i < 256; i++)
+    {
+       pn_draw_line(x_pos[i - 1], y_pos[i - 1], x_pos[i], y_pos[i], value);
+
+       if ( opts[0].val.ival == 0 )
+         pn_draw_line(x2_pos[i - 1], y2_pos[i - 1], x2_pos[i], y2_pos[i], value);
+    }
+
+  g_free(x_pos);
+  g_free(y_pos);
+  g_free(x2_pos);
+  g_free(y2_pos);
+}
+
+static void
+wave_horizontal_exec (const struct pn_actuator_option *opts,
+		    gpointer data)
+{
+  if (opts[2].val.bval == TRUE)
+    wave_horizontal_exec_lines(opts, data);
+  else
+    wave_horizontal_exec_dots(opts, data);
+}
+
+struct pn_actuator_desc builtin_wave_horizontal =
+{
+  "wave_horizontal", "Horizontal Waveform",
+  "Draws one or two waveforms horizontally across "
+  "the drawing surface",
+  0, wave_horizontal_opts,
+  NULL, NULL, wave_horizontal_exec
+};
+
+/* **************** wave_vertical **************** */
+struct pn_actuator_option_desc wave_vertical_opts[] =
+{
+  {"channels", "Which sound channels to use: negative = channel 1, \npositive = channel 2, "
+   "zero = both (two wave-forms.)", OPT_TYPE_INT, {ival: -1} },
+  {"value", "The colour value to use.", OPT_TYPE_INT, {ival: 255} },
+  {"lines", "Use lines instead of dots.", OPT_TYPE_BOOLEAN, {bval: TRUE} },
+  { NULL }
+};
+
+static void
+wave_vertical_exec_dots (const struct pn_actuator_option *opts,
+		    gpointer data)
+{
+  int i;
+  int channel = ( opts[0].val.ival < 0 ) ? 0 : 1;
+  guchar value = (opts[1].val.ival < 0 || opts[1].val.ival > 255) ? 255 : opts[1].val.ival;
+
+  for (i=0; i<pn_image_data->height; i++) {
+    if ( opts[0].val.ival ) {
+      pn_image_data->surface[0][PN_IMG_INDEX ((pn_image_data->width>>1)
+					      - CAP (pn_sound_data->pcm_data[channel]
+						     [i*512/pn_image_data->height]>>8,
+						     (pn_image_data->width>>1)-1), i)]
+	= value;
+    }
+    else {
+      pn_image_data->surface[0][PN_IMG_INDEX ((pn_image_data->width>>2) 
+					      - CAP (pn_sound_data->pcm_data[0]
+						     [i*512/pn_image_data->height]>>9,
+						     (pn_image_data->width>>2)-1), i)]
+	= value;
+      pn_image_data->surface[0][PN_IMG_INDEX ((3*pn_image_data->width>>2) 
+						 -CAP (pn_sound_data->pcm_data[1]
+						       [i*512/pn_image_data->height]>>9,
+						       (pn_image_data->width>>2)-1), i)]
+			       
+	= value;
+    }
+  }
+}
+
+static void
+wave_vertical_exec_lines (const struct pn_actuator_option *opts,
+		    gpointer data)
+{
+  int channel = ( opts[0].val.ival < 0 ) ? 0 : 1;
+  guchar value = (opts[1].val.ival < 0 || opts[1].val.ival > 255) ? 255 : opts[1].val.ival;
+  int *x_pos, *y_pos;	/* dynamic tables which store the positions for the line */
+  int *x2_pos, *y2_pos;	/* dynamic tables which store the positions for the line */
+  int i;
+  float step;
+
+  x_pos = g_new0(int, 129);
+  y_pos = g_new0(int, 129);
+  x2_pos = g_new0(int, 129);
+  y2_pos = g_new0(int, 129);
+
+  step = pn_image_data->height / 128.;
+
+  /* calculate the line. */
+  for (i = 0; i < 128; i++)
+    {
+      if (opts[0].val.ival != 0)
+        {
+           x_pos[i] = (pn_image_data->width>>1) - 
+			CAP (pn_sound_data->pcm_data[channel]
+			[i*4]>>8,
+			(pn_image_data->width>>1)-1);
+	   y_pos[i] = i * step;
+        }
+      else
+        {
+           x_pos[i] = (pn_image_data->width>>2) 
+		      - CAP (pn_sound_data->pcm_data[0]
+		     [i*4]>>9,
+		     (pn_image_data->width>>2)-1);
+           y_pos[i] = i * step;
+
+           x2_pos[i] = 3*(pn_image_data->width>>2) 
+		      - CAP (pn_sound_data->pcm_data[1]
+		     [i*4]>>9,
+		     (pn_image_data->width>>2)-1);
+           y2_pos[i] = i * step;
+        }
+    }
+
+  /* draw the line. */
+  for (i = 1; i < 128; i++)
+    {
+       pn_draw_line(x_pos[i - 1], y_pos[i - 1], x_pos[i], y_pos[i], value);
+
+       if ( opts[0].val.ival == 0 )
+         pn_draw_line(x2_pos[i - 1], y2_pos[i - 1], x2_pos[i], y2_pos[i], value);
+    }
+
+  g_free(x_pos);
+  g_free(y_pos);
+  g_free(x2_pos);
+  g_free(y2_pos);
+}
+
+static void
+wave_vertical_exec (const struct pn_actuator_option *opts,
+		    gpointer data)
+{
+  if (opts[2].val.bval == TRUE)
+    wave_vertical_exec_lines(opts, data);
+  else
+    wave_vertical_exec_dots(opts, data);
+}
+
+struct pn_actuator_desc builtin_wave_vertical =
+{
+  "wave_vertical", "Vertical Waveform",
+  "Draws one or two waveforms vertically across "
+  "the drawing surface",
+  0, wave_vertical_opts,
+  NULL, NULL, wave_vertical_exec
+};
+
+/* FIXME: allow for only 1 channel for wave_normalize & wave_smooth */
+/* **************** wave_normalize **************** */
+static struct pn_actuator_option_desc wave_normalize_opts[] =
+{
+  { "height", "If positive, the height, in pixels, to which the waveform will be "
+    "normalized; if negative, hfrac is used", OPT_TYPE_INT, { ival: -1 } },
+  { "hfrac", "If positive, the fraction of the horizontal image size to which the "
+    "waveform will be normalized; if negative, vfrac is used",
+    OPT_TYPE_FLOAT, { fval: -1 } },
+  { "vfrac", "If positive, the fraction of the vertical image size to which the "
+    "waveform will be normalized",
+    OPT_TYPE_FLOAT, { fval: .125 } },
+  { "channels", "Which sound channel(s) to normalize: negative = channel 1,\n"
+    "\tpositive = channel 2, 0 = both channels.",
+    OPT_TYPE_INT, { ival: 0 } },
+  { NULL }
+};
+
+static void
+wave_normalize_exec (const struct pn_actuator_option *opts,
+		     gpointer data)
+{
+  int i, j, max=0;
+  float denom;
+
+  for (j=0; j<2; j++)
+    {
+      if ( !(opts[3].val.ival) || (opts[3].val.ival < 0 && j == 0) ||
+	   (opts[3].val.ival > 0 && j == 1) ) {
+	
+	for (i=0; i<512; i++)
+	  if (abs(pn_sound_data->pcm_data[j][i]) > max)
+	    max = abs(pn_sound_data->pcm_data[j][i]);
+	
+	if (opts[0].val.ival > 0)
+	  denom = max/(opts[0].val.ival<<8);
+	else if (opts[1].val.fval > 0)
+	  denom = max/(opts[1].val.fval * (pn_image_data->width<<8));
+	else
+	  denom = max/(opts[2].val.fval * (pn_image_data->height<<8));
+	
+	if (denom > 0)
+	  for (i=0; i<512; i++)
+	    pn_sound_data->pcm_data[j][i]
+	      /= denom;
+      }
+    }
+}
+
+struct pn_actuator_desc builtin_wave_normalize =
+{
+  "wave_normalize", "Normalize Waveform Data",
+  "Normalizes the waveform data used by the wave_* actuators",
+  0, wave_normalize_opts,
+  NULL, NULL, wave_normalize_exec
+};
+
+/* **************** wave_smooth **************** */
+struct pn_actuator_option_desc wave_smooth_opts[] = 
+{
+  { "channels", "Which sound channel(s) to smooth: negative = channel 1, \n"
+    "\tpositive = channel 2, 0 = both channels.",
+    OPT_TYPE_INT, { ival: 0 } },
+  {0}
+};
+
+static void
+wave_smooth_exec (const struct pn_actuator_option *opts,
+		  gpointer data)
+{
+  int i, j, k;
+  gint16 tmp[512];
+
+  for (j=0; j<2; j++)
+    {
+      if ( !(opts[0].val.ival) || (opts[0].val.ival < 0 && j == 0) ||
+	   (opts[0].val.ival > 0 && j == 1) ) { 
+	
+	for (i=4; i<508; i++)
+	  {
+	    k = (pn_sound_data->pcm_data[j][i]<<3)
+	      + (pn_sound_data->pcm_data[j][i+1]<<2)
+	      + (pn_sound_data->pcm_data[j][i-1]<<2)
+	      + (pn_sound_data->pcm_data[j][i+2]<<2)
+	      + (pn_sound_data->pcm_data[j][i-2]<<2)
+	      + (pn_sound_data->pcm_data[j][i+3]<<1)
+	      + (pn_sound_data->pcm_data[j][i-3]<<1)
+	      + (pn_sound_data->pcm_data[j][i+4]<<1)
+	      + (pn_sound_data->pcm_data[j][i-4]<<1);
+	    tmp[i] = k >> 5;
+	  }
+	memcpy (pn_sound_data->pcm_data[j]+4, tmp, sizeof (gint16) * 504);
+      } 
+    }
+}
+
+struct pn_actuator_desc builtin_wave_smooth =
+{
+  "wave_smooth", "Smooth Waveform Data",
+  "Smooth out the waveform data used by the wave_* actuators",
+  0, wave_smooth_opts,
+  NULL, NULL, wave_smooth_exec
+};
+
+/* **************** wave_radial **************** */
+static struct pn_actuator_option_desc wave_radial_opts[] =
+{
+  { "base_radius", " ", 
+    OPT_TYPE_FLOAT, { fval: 0 } },
+  {"value", "The colour value to use.", OPT_TYPE_INT, {ival: 255} },
+/*  {"lines", "Use lines instead of dots.", OPT_TYPE_BOOLEAN, {bval: TRUE} }, */
+  { NULL }
+};
+
+static void
+wave_radial_exec (const struct pn_actuator_option *opts,
+		  gpointer data)
+{
+  int i, x, y;
+  guchar value = (opts[1].val.ival < 0 || opts[1].val.ival > 255) ? 255 : opts[1].val.ival;
+  
+  for(i=0; i<360; i++)
+    {
+      x = (pn_image_data->width>>1)
+	+ (opts[0].val.fval + (pn_sound_data->pcm_data[0][(int)(i*(512.0/360.0))]>>8))
+	* cos_val[i];
+      y = (pn_image_data->height>>1) 
+	+ (opts[0].val.fval + (pn_sound_data->pcm_data[0][(int)(i*(512.0/360.0))]>>8))
+	* sin_val[i];
+
+      pn_image_data->surface[0][PN_IMG_INDEX (CAPHILO(x,pn_image_data->width,0),
+					      CAPHILO(y,pn_image_data->height,0))]
+	= value;
+    }
+};	       
+
+struct pn_actuator_desc builtin_wave_radial =
+{
+  "wave_radial", "Radial Waveform",
+  "Draws a single waveform varying"
+  " radially from the center of the image",
+  0, wave_radial_opts,
+  NULL, NULL, wave_radial_exec
+};
+
+/* **************** wave_scope **************** */
+
+static struct pn_actuator_option_desc wave_scope_opts[] =
+{
+  {"init_script", "Initialization script.", OPT_TYPE_STRING, {sval: "n = 800; t = -0.05;"} },
+  {"frame_script", "Script to run at the beginning of each frame.", OPT_TYPE_STRING, {sval: "t = t + 0.05;"} },
+  {"sample_script", "Script to run for each sample.", OPT_TYPE_STRING, {sval: "d = index + value; r = t + index * 3.141952924 * 4; x = cos(r) * d; y = sin(r) * d;"} },
+  {"lines", "Use lines instead of dots.", OPT_TYPE_BOOLEAN, {bval: TRUE} },
+  { NULL }
+};
+
+struct pn_scope_data
+{
+  expression_t *expr_on_init, *expr_on_frame, *expr_on_sample;
+  symbol_dict_t *dict;
+  gboolean reset;
+};
+
+static void
+wave_scope_init(gpointer *data)
+{
+  *data = g_new0(struct pn_scope_data, 1);
+
+  /* the expressions will need to be compiled, so prepare for that */
+  ((struct pn_scope_data *)*data)->reset = TRUE;
+}
+
+static void
+wave_scope_cleanup(gpointer op_data)
+{
+  struct pn_scope_data *data = (struct pn_scope_data *) op_data;
+
+  g_return_if_fail(data != NULL);
+
+  if (data->expr_on_init)
+    expr_free(data->expr_on_init);
+
+  if (data->expr_on_frame)
+    expr_free(data->expr_on_frame);
+
+  if (data->expr_on_sample)
+    expr_free(data->expr_on_sample);
+
+  if (data->dict)
+    dict_free(data->dict);
+
+  if (data)
+    g_free(data);
+}
+
+static void
+wave_scope_exec(const struct pn_actuator_option *opts,
+		gpointer op_data)
+{
+  struct pn_scope_data *data = (struct pn_scope_data *) op_data;
+  gint i;
+  gdouble *xf, *yf, *index, *value, *points;
+
+  if (data->reset)
+    {
+       if (data->dict)
+         dict_free(data->dict);
+
+       data->dict = dict_new();
+
+       if (opts[0].val.sval != NULL)
+         data->expr_on_init = expr_compile_string(opts[0].val.sval, data->dict);
+
+       if (opts[1].val.sval != NULL)
+         data->expr_on_frame = expr_compile_string(opts[1].val.sval, 
+		data->dict);
+
+       if (opts[2].val.sval != NULL)
+         data->expr_on_sample = expr_compile_string(opts[2].val.sval, 
+		data->dict);
+
+       if (data->expr_on_init != NULL)
+         expr_execute(data->expr_on_init, data->dict);
+
+       data->reset = FALSE;
+    }
+
+  xf = dict_variable(data->dict, "x");
+  yf = dict_variable(data->dict, "y");
+  index = dict_variable(data->dict, "index");
+  value = dict_variable(data->dict, "value");
+  points = dict_variable(data->dict, "points");
+
+  if (data->expr_on_frame != NULL)
+    expr_execute(data->expr_on_frame, data->dict);
+
+  if (*points > 513 || *points == 0)
+      *points = 513;
+
+  if (data->expr_on_sample != NULL)
+    {
+       for (i = 0; i < *points; i++)
+          {
+             gint x, y;
+             static gint oldx, oldy;
+
+             *value = 1.0 * pn_sound_data->pcm_data[0][i & 511] / 32768.0;
+             *index = i / (*points - 1);
+
+             expr_execute(data->expr_on_sample, data->dict);
+
+             x = (gint)(((*xf + 1.0) * (pn_image_data->width - 1) / 2) + 0.5);
+             y = (gint)(((*yf + 1.0) * (pn_image_data->height - 1) / 2) + 0.5);
+
+             if (i != 0)
+               pn_draw_line(oldx, oldy, x, y, 255);
+
+             oldx = x;
+             oldy = y;
+          }
+    }
+}
+
+struct pn_actuator_desc builtin_wave_scope =
+{
+  "wave_scope", "Scope",
+  "A programmable scope.",
+  0, wave_scope_opts,
+  wave_scope_init, wave_scope_cleanup, wave_scope_exec
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/rovascope/xform.c	Sat Jan 06 01:57:34 2007 -0800
@@ -0,0 +1,764 @@
+/* FIXME: allow for only using an xform on part of the img? */
+/* FIXME: perhaps combine these into a single vector field
+   so that only 1 apply_xform needs to be done for as many
+   of these as someone wants to use */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <math.h>
+
+#include <glib.h>
+
+#include "paranormal.h"
+#include "actuators.h"
+#include "pn_utils.h"
+
+#include "libcalc/calc.h"
+
+struct xform_vector
+{
+  gint32 offset; /* the offset of the top left pixel */
+  guint16 w; /* 4:4:4:4 NE, NW, SE, SW pixel weights
+		  The total should be 16 */
+
+  /* if offset < 0 then w is the color index to
+     which the pixel will be set */
+};
+
+static void
+xfvec (float x, float y, struct xform_vector *v)
+{
+  float xd, yd;
+  int weight[4];
+
+  if (x >= pn_image_data->width-1 || y >= pn_image_data->height-1
+      || x < 0 || y < 0)
+    {
+      v->offset = -1;
+      v->w = 0;
+      return;
+    }
+
+  v->offset = PN_IMG_INDEX (floor(x), floor(y));
+
+  xd = x - floor (x);
+  yd = y - floor (y);
+
+  weight[3] = xd * yd * 16;
+  weight[2] = (1-xd) * yd * 16;
+  weight[1] = xd * (1-yd) * 16;
+  weight[0] = 16 - weight[3] - weight[2] - weight[1]; /* just in case */
+
+  v->w = (weight[0]<<12) | (weight[1]<<8) | (weight[2]<<4) | weight[3];
+}
+
+static void
+apply_xform (struct xform_vector *vfield)
+{
+  int i;
+  struct xform_vector *v;
+  register guchar *srcptr;
+  register guchar *destptr;
+  register int color;
+
+  if (vfield == NULL)
+      return;
+
+  for (i=0, v=vfield, destptr=pn_image_data->surface[1];
+       i<pn_image_data->width*pn_image_data->height;
+       i++, v++, destptr++)
+    {
+      /* off the screen */
+      if (v->offset < 0)
+	{
+	  *destptr = (guchar)v->w;
+	  continue;
+	}
+
+      srcptr = pn_image_data->surface[0] + v->offset;
+
+      /* exactly on the pixel */
+      if (v->w == 0)
+	  *destptr = *srcptr;
+      
+      /* gotta blend the points */
+      else
+	{
+	  color =  *srcptr * (v->w>>12);
+	  color += *++srcptr * ((v->w>>8) & 0x0f);
+	  color += *(srcptr+=pn_image_data->width) * (v->w & 0x0f);
+	  color += *(--srcptr) * ((v->w>>4) & 0x0f);
+	  color >>= 4;
+	  *destptr = (guchar)color;
+	}
+    }
+}
+
+/* **************** xform_spin **************** */
+/* FIXME: Describe these better, how they are backwards */
+/* FIXME: better name? */
+struct pn_actuator_option_desc xform_spin_opts[] =
+{
+  { "angle", "The angle of rotation", OPT_TYPE_FLOAT, { fval: -8.0 } },
+  { "r_add", "The number of pixels by which the r coordinate will be "
+    "increased (before scaling)", OPT_TYPE_FLOAT, { fval: 0.0 } },
+  { "r_scale", "The amount by which the r coordinate of each pixel will "
+    "be scaled", OPT_TYPE_FLOAT, { fval: 1.0 } },
+  { NULL }
+};
+
+struct xform_spin_data
+{
+  int width, height;
+  struct xform_vector *vfield;
+};
+
+static void
+xform_spin_init (gpointer *data)
+{
+  *data = g_new0 (struct xform_spin_data, 1);
+}
+
+static void
+xform_spin_cleanup (gpointer data)
+{
+  struct xform_spin_data *d = (struct xform_spin_data *) data;
+
+  
+  if (d)
+    {
+      if (d->vfield)
+	g_free (d->vfield);
+      g_free (d);
+    }
+}
+
+static void
+xform_spin_exec (const struct pn_actuator_option *opts,
+		 gpointer data)
+{
+  struct xform_spin_data *d = (struct xform_spin_data*)data;
+  float i, j;
+
+  if (d->width != pn_image_data->width
+      || d->height != pn_image_data->height)
+    {
+      d->width = pn_image_data->width;
+      d->height = pn_image_data->height;
+
+      if (d->vfield)
+	g_free (d->vfield);
+
+      d->vfield = g_malloc0 (sizeof(struct xform_vector)
+			    * d->width * d->height);
+
+      for (j=-(pn_image_data->height>>1)+1; j<=pn_image_data->height>>1; j++)
+	for (i=-(pn_image_data->width>>1); i<pn_image_data->width>>1; i++)
+	  {
+	    float r, t = 0;
+	    float x, y;
+
+	    r = sqrt (i*i + j*j);
+	    if (r)
+	      t = asin (j/r);
+	    if (i < 0)
+	      t = M_PI - t;
+
+	    t += opts[0].val.fval * M_PI/180.0; 
+	    r += opts[1].val.fval;
+	    r *= opts[2].val.fval;
+
+	    x = (r * cos (t)) + (pn_image_data->width>>1);
+	    y = (pn_image_data->height>>1) - (r * sin (t));
+
+	    xfvec (x, y, &d->vfield
+		   [PN_IMG_INDEX ((pn_image_data->width>>1)+(int)rint(i),
+				  ((pn_image_data->height>>1)-(int)rint(j)))]);
+	  }
+    }
+
+  apply_xform (d->vfield);
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_xform_spin =
+{
+  "xform_spin", "Spin Transform", 
+  "Rotates and radially scales the image",
+  0, xform_spin_opts,
+  xform_spin_init, xform_spin_cleanup, xform_spin_exec
+};
+
+/* **************** xform_ripple **************** */
+struct pn_actuator_option_desc xform_ripple_opts[] =
+{
+  { "angle", "The angle of rotation", OPT_TYPE_FLOAT, { fval: 0 } },
+  { "ripples", "The number of ripples that fit on the screen "
+    "(horizontally)", OPT_TYPE_FLOAT, { fval: 8 } },
+  { "base_speed", "The minimum number of pixels to move each pixel",
+    OPT_TYPE_FLOAT, { fval: 1 } },
+  { "mod_speed", "The maximum number of pixels by which base_speed"
+    " will be modified", OPT_TYPE_FLOAT, { fval: 1 } },
+  { NULL }
+};
+
+struct xform_ripple_data
+{
+  int width, height;
+  struct xform_vector *vfield;
+};
+
+static void
+xform_ripple_init (gpointer *data)
+{
+  *data = g_new0 (struct xform_ripple_data, 1);
+}
+
+static void
+xform_ripple_cleanup (gpointer data)
+{
+  struct xform_ripple_data *d = (struct xform_ripple_data*) data;
+
+  if (d)
+    {
+      if (d->vfield)
+	g_free (d->vfield);
+      g_free (d);
+    }
+}
+
+static void
+xform_ripple_exec (const struct pn_actuator_option *opts,
+		 gpointer data)
+{
+  struct xform_ripple_data *d = (struct xform_ripple_data*)data;
+  float i, j;
+
+  if (d->width != pn_image_data->width
+      || d->height != pn_image_data->height)
+    {
+      d->width = pn_image_data->width;
+      d->height = pn_image_data->height;
+
+      if (d->vfield)
+	g_free (d->vfield);
+
+      d->vfield = g_malloc (sizeof(struct xform_vector)
+			    * d->width * d->height);
+
+      for (j=-(pn_image_data->height>>1)+1; j<=pn_image_data->height>>1; j++)
+	for (i=-(pn_image_data->width>>1); i<pn_image_data->width>>1; i++)
+	  {
+	    float r, t = 0;
+	    float x, y;
+
+	    r = sqrt (i*i + j*j);
+	    if (r)
+	      t = asin (j/r);
+	    if (i < 0)
+	      t = M_PI - t;
+
+	    t += opts[0].val.fval * M_PI/180.0; 
+
+	    if (r > 4)//(pn_image_data->width/(2*opts[1].val.fval)))
+	      r -=  opts[2].val.fval + (opts[3].val.fval/2) *
+		(1 + sin ((r/(pn_image_data->width/(2*opts[1].val.fval)))*M_PI));
+/*  	    else if (r > 4) */
+/*  	      r *= r/(pn_image_data->width/opts[1].val.fval); */
+	    else /* don't let it explode */
+	      r = 1000000;
+
+
+	    x = (r * cos (t)) + (pn_image_data->width>>1);
+	    y = (pn_image_data->height>>1) - (r * sin (t));
+
+	    xfvec (x, y, &d->vfield
+		   [PN_IMG_INDEX ((pn_image_data->width>>1)+(int)rint(i),
+				  ((pn_image_data->height>>1)-(int)rint(j)))]);
+	  }
+    }
+
+  apply_xform (d->vfield);
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_xform_ripple =
+{
+  "xform_ripple", "Ripple Transform", "Creates an ripple effect",
+  0, xform_ripple_opts,
+  xform_ripple_init, xform_ripple_cleanup, xform_ripple_exec
+};
+
+/* **************** xform_bump_spin **************** */
+struct pn_actuator_option_desc xform_bump_spin_opts[] =
+{
+  { "angle", "The angle of rotation", OPT_TYPE_FLOAT, { fval: 0 } },
+  { "bumps", "The number of bumps that on the image",
+    OPT_TYPE_FLOAT, { fval: 8 } },
+  { "base_scale", "The base radial scale",
+    OPT_TYPE_FLOAT, { fval: 0.95 } },
+  { "mod_scale", "The maximum amount that should be "
+    "added to the base_scale to create the 'bump' effect",
+    OPT_TYPE_FLOAT, { fval: .1 } },
+  { NULL }
+};
+
+struct xform_bump_spin_data
+{
+  int width, height;
+  struct xform_vector *vfield;
+};
+
+static void
+xform_bump_spin_init (gpointer *data)
+{
+  *data = g_new0 (struct xform_bump_spin_data, 1);
+}
+
+static void
+xform_bump_spin_cleanup (gpointer data)
+{
+  struct xform_bump_spin_data *d = (struct xform_bump_spin_data*) data;
+
+  if (d)
+    {
+      if (d->vfield)
+	g_free (d->vfield);
+      g_free (d);
+    }
+}
+
+static void
+xform_bump_spin_exec (const struct pn_actuator_option *opts,
+		 gpointer data)
+{
+  struct xform_bump_spin_data *d = (struct xform_bump_spin_data*)data;
+  float i, j;
+
+  if (d->width != pn_image_data->width
+      || d->height != pn_image_data->height)
+    {
+      d->width = pn_image_data->width;
+      d->height = pn_image_data->height;
+
+      if (d->vfield)
+	g_free (d->vfield);
+
+      d->vfield = g_malloc (sizeof(struct xform_vector)
+			    * d->width * d->height);
+
+      for (j=-(pn_image_data->height>>1)+1; j<=pn_image_data->height>>1; j++)
+	for (i=-(pn_image_data->width>>1); i<pn_image_data->width>>1; i++)
+	  {
+	    float r, t = 0;
+	    float x, y;
+
+	    r = sqrt (i*i + j*j);
+	    if (r)
+	      t = asin (j/r);
+	    if (i < 0)
+	      t = M_PI - t;
+
+	    t += opts[0].val.fval * M_PI/180.0;
+
+	    r *=  opts[2].val.fval + opts[3].val.fval
+	      * (1 + sin (t*opts[1].val.fval));
+
+	    x = (r * cos (t)) + (pn_image_data->width>>1);
+	    y = (pn_image_data->height>>1) - (r * sin (t));
+
+	    xfvec (x, y, &d->vfield
+		   [PN_IMG_INDEX ((pn_image_data->width>>1)+(int)rint(i),
+				  ((pn_image_data->height>>1)-(int)rint(j)))]);
+	  }
+    }
+
+  apply_xform (d->vfield);
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_xform_bump_spin =
+{
+  "xform_bump_spin", "Bump Transform", 
+  "Rotate the image at a varying speed to create "
+  "the illusion of bumps",
+  0, xform_bump_spin_opts,
+  xform_bump_spin_init, xform_bump_spin_cleanup, xform_bump_spin_exec
+};
+
+/* **************** xform_halfrender **************** */
+struct pn_actuator_option_desc xform_halfrender_opts[] =
+{
+  { "direction", "Negative is horizontal, positive is vertical.", 
+    OPT_TYPE_INT, { ival: 1 } },
+  { "render_twice", "Render the second image.",
+    OPT_TYPE_BOOLEAN, { bval: TRUE } },
+  { NULL }
+};
+
+static void
+xform_halfrender_exec (const struct pn_actuator_option *opts,
+		 gpointer data)
+{
+  gint x, y;
+
+  if (opts[0].val.ival < 0)
+    {
+       for (y = 0; y < pn_image_data->height; y += 2)
+         {
+            for (x = 0; x < pn_image_data->width; x++)
+              {
+                 pn_image_data->surface[1][PN_IMG_INDEX(x, y / 2)] =
+                   pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
+	         if (opts[1].val.bval)
+                   {
+                     pn_image_data->surface[1][PN_IMG_INDEX(x, (y / 2) + (pn_image_data->height / 2))] =
+                       pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
+                   }
+              }
+         }
+    }
+  else
+    {
+       for (y = 0; y < pn_image_data->height; y++)
+         {
+            for (x = 0; x < pn_image_data->width; x += 2)
+              {
+                 pn_image_data->surface[1][PN_IMG_INDEX(x / 2, y)] =
+                   pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
+                 if (opts[1].val.bval)
+                   {
+                     pn_image_data->surface[1][PN_IMG_INDEX((x / 2) + (pn_image_data->width / 2), y)] =
+                       pn_image_data->surface[0][PN_IMG_INDEX(x, y)];
+                   }
+              }
+         }
+    }
+
+    pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_xform_halfrender =
+{
+  "xform_halfrender", "Halfrender Transform",
+  "Divides the surface in half and renders it twice.",
+  0, xform_halfrender_opts,
+  NULL, NULL, xform_halfrender_exec
+};
+
+/* **************** xform_movement **************** */
+struct pn_actuator_option_desc xform_movement_opts[] =
+{
+  { "formula", "The formula to evaluate.",
+    OPT_TYPE_STRING, { sval: "r = r * cos(r); d = sin(d);" } },
+  { "polar", "Whether the coordinates are polar or not.",
+    OPT_TYPE_BOOLEAN, { bval: TRUE } },
+  { NULL }
+};
+
+typedef struct {
+  int width, height;                 /* Previous width and height. */
+  struct xform_vector *vfield;
+} PnMovementData;
+
+static void
+xform_movement_init (gpointer *data)
+{
+    *data = g_new0(PnMovementData, 1);
+}
+
+static void
+xform_movement_cleanup (gpointer data)
+{
+    PnMovementData *d = (PnMovementData *) data;
+
+    if (d)
+      {
+         if (d->vfield)
+ 	     g_free (d->vfield);
+         g_free (d);
+      }
+}
+
+inline void
+xform_trans_polar (struct xform_vector *vfield, gint x, gint y,
+	expression_t *expr, symbol_dict_t *dict)
+{
+  gdouble *rf, *df;
+  gdouble xf, yf;
+  gint xn, yn;
+
+  rf = dict_variable(dict, "r");
+  df = dict_variable(dict, "d");
+
+  /* Points (xf, yf) must be in a (-1..1) square. */
+  xf = 2.0 * x / (pn_image_data->width - 1) - 1.0;
+  yf = 2.0 * y / (pn_image_data->height - 1) - 1.0;
+
+  /* Now, convert to polar coordinates r and d. */
+  *rf = hypot(xf, yf);
+  *df = atan2(yf, xf);
+
+  /* Run the script. */
+  expr_execute(expr, dict);
+
+  /* Back to (-1..1) square. */
+  xf = (*rf) * cos ((*df));
+  yf = (*rf) * sin ((*df));
+
+  /* Convert back to physical coordinates. */
+  xn = (int)(((xf + 1.0) * (pn_image_data->width - 1) / 2) + 0.5);
+  yn = (int)(((yf + 1.0) * (pn_image_data->height - 1) / 2) + 0.5);
+
+  if (xn < 0 || xn >= pn_image_data->width || yn < 0 || yn >= pn_image_data->height)
+    {
+      xn = x; yn = y;
+    }
+
+  xfvec (xn, yn, &vfield[PN_IMG_INDEX (x, y)]);
+}
+
+inline void
+xform_trans_literal (struct xform_vector *vfield, gint x, gint y,
+	expression_t *expr, symbol_dict_t *dict)
+{
+  gdouble rf, df;
+  gdouble *xf, *yf;
+  gint xn, yn;
+
+  xf = dict_variable(dict, "x");
+  yf = dict_variable(dict, "y");
+
+  /* Points (xf, yf) must be in a (-1..1) square. */
+  *xf = 2.0 * x / (pn_image_data->width - 1) - 1.0;
+  *yf = 2.0 * y / (pn_image_data->height - 1) - 1.0;
+
+  /* Run the script. */
+  expr_execute(expr, dict);
+
+  /* Convert back to physical coordinates. */
+  xn = (int)(((*xf + 1.0) * (pn_image_data->width - 1) / 2) + 0.5);
+  yn = (int)(((*yf + 1.0) * (pn_image_data->height - 1) / 2) + 0.5);
+
+  if (xn < 0 || xn >= pn_image_data->width || yn < 0 || yn >= pn_image_data->height)
+    {
+      xn = x; yn = y;
+    }
+
+  xfvec (xn, yn, &vfield[PN_IMG_INDEX (x, y)]);
+}
+
+static void
+xform_movement_exec (const struct pn_actuator_option *opts,
+		 gpointer odata)
+{
+  PnMovementData *d = (PnMovementData *) odata;
+  void (*transform_func)(struct xform_vector *, gint, gint, expression_t *, symbol_dict_t *) = 
+        opts[1].val.bval == TRUE ? xform_trans_polar : xform_trans_literal;
+
+  if (d->width != pn_image_data->width
+      || d->height != pn_image_data->height)
+    {
+      gint i, j;
+      gdouble *rf, *df;
+      gdouble xf, yf;
+      gint xn, yn;
+      expression_t *expr;
+      symbol_dict_t *dict;
+
+      d->width = pn_image_data->width;
+      d->height = pn_image_data->height;
+
+      if (d->vfield)
+        {
+  	  g_free (d->vfield);
+          d->vfield = NULL;
+        }
+
+      if (opts[0].val.sval == NULL)
+        return;
+
+      dict = dict_new();
+      expr = expr_compile_string(opts[0].val.sval, dict);
+      if (!expr)
+        {
+           dict_free(dict);
+           return;
+        }
+
+      rf = dict_variable(dict, "r");
+      df = dict_variable(dict, "d");
+
+      d->vfield = g_malloc (sizeof(struct xform_vector)
+			    * d->width * d->height);
+
+      for (j = 0; j < pn_image_data->height; j++)
+	for (i = 0; i < pn_image_data->width; i++)
+	  {
+            transform_func(d->vfield, i, j, expr, dict);
+	  }
+    }
+
+  apply_xform (d->vfield);
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_xform_movement =
+{
+  "xform_movement", "Movement Transform",
+  "A customizable blitter.",
+  0, xform_movement_opts,
+  xform_movement_init, xform_movement_cleanup, xform_movement_exec
+};
+
+/* **************** xform_dynmovement **************** */
+/* FIXME: really slow */
+struct pn_actuator_option_desc xform_dynmovement_opts[] =
+{
+  { "init_script", "The formula to evaluate on init.",
+    OPT_TYPE_STRING, { sval: "" } },
+  { "beat_script", "The formula to evaluate on each beat.",
+    OPT_TYPE_STRING, { sval: "" } },
+  { "frame_script", "The formula to evaluate on each frame.",
+    OPT_TYPE_STRING, { sval: "" } },
+  { "point_script", "The formula to evaluate.",
+    OPT_TYPE_STRING, { sval: "d = 0.15;" } },
+  { "polar", "Whether or not the coordinates to use are polar.",
+    OPT_TYPE_BOOLEAN, { bval: TRUE } },
+  { NULL }
+};
+
+typedef struct {
+  int width, height;                 /* Previous width and height. */
+  expression_t *expr_init;
+  expression_t *expr_frame;
+  expression_t *expr_beat;
+  expression_t *expr_point;
+  symbol_dict_t *dict;
+  struct xform_vector *vfield;
+} PnDynMovementData;
+
+static void
+xform_dynmovement_init (gpointer *data)
+{
+    *data = g_new0(PnDynMovementData, 1);
+}
+
+static void
+xform_dynmovement_cleanup (gpointer data)
+{
+    PnDynMovementData *d = (PnDynMovementData *) data;
+
+    if (d)
+      {
+         if (d->expr_init)
+             expr_free (d->expr_init);
+         if (d->expr_beat)
+             expr_free (d->expr_beat);
+         if (d->expr_frame)
+             expr_free (d->expr_frame);
+         if (d->expr_point)
+             expr_free (d->expr_point);
+         if (d->dict)
+             dict_free (d->dict);
+         if (d->vfield)
+ 	     g_free (d->vfield);
+         g_free (d);
+      }
+}
+
+static void
+xform_dynmovement_exec (const struct pn_actuator_option *opts,
+		 gpointer odata)
+{
+  PnDynMovementData *d = (PnDynMovementData *) odata;
+  gint i, j;
+  gdouble *rf, *df;
+  gdouble xf, yf;
+  gint xn, yn;
+  void (*transform_func)(struct xform_vector *, gint, gint, expression_t *, symbol_dict_t *) = 
+        opts[4].val.bval == TRUE ? xform_trans_polar : xform_trans_literal;
+  gboolean make_table = FALSE;
+
+  if (d->width != pn_image_data->width
+      || d->height != pn_image_data->height)
+    {
+      d->width = pn_image_data->width;
+      d->height = pn_image_data->height;
+
+      if (d->vfield)
+        {
+  	  g_free (d->vfield);
+          d->vfield = NULL;
+        }
+
+      if (opts[3].val.sval == NULL)
+          return;
+
+      if (!d->dict)
+          d->dict = dict_new();
+      else
+        {
+          dict_free(d->dict);
+          d->dict = dict_new();
+        }
+
+      if (d->expr_init)
+        {
+          expr_free(d->expr_init);
+          d->expr_init = NULL;
+        }
+
+      /* initialize */
+      d->expr_init = expr_compile_string(opts[0].val.sval, d->dict);
+
+      if (d->expr_init != NULL)
+        {
+           expr_execute(d->expr_init, d->dict);
+        }
+
+      d->expr_beat = expr_compile_string(opts[1].val.sval, d->dict);
+      d->expr_frame = expr_compile_string(opts[2].val.sval, d->dict);
+      d->expr_point = expr_compile_string(opts[3].val.sval, d->dict);
+
+      d->vfield = g_malloc (sizeof(struct xform_vector)
+			    * d->width * d->height);
+
+      make_table = TRUE;
+   }
+
+   rf = dict_variable(d->dict, "r");
+   df = dict_variable(d->dict, "d");
+
+   if (*opts[2].val.sval != '\0' || pn_new_beat)
+       make_table = TRUE;
+
+   /* run the on-frame script. */
+   if (make_table == TRUE)
+     {
+       if (d->expr_beat != NULL)
+         expr_execute(d->expr_beat, d->dict);
+
+       if (d->expr_frame != NULL)
+         expr_execute(d->expr_frame, d->dict);
+
+       for (j = 0; j < pn_image_data->height; j++)
+         for (i = 0; i < pn_image_data->width; i++)
+           {
+             transform_func(d->vfield, i, j, d->expr_point, d->dict);
+           }
+     }
+
+  apply_xform (d->vfield);
+  pn_swap_surfaces ();
+}
+
+struct pn_actuator_desc builtin_xform_dynmovement =
+{
+  "xform_dynmovement", "Dynamic Movement Transform",
+  "A customizable blitter.",
+  0, xform_dynmovement_opts,
+  xform_dynmovement_init, xform_dynmovement_cleanup, xform_dynmovement_exec
+};