# HG changeset patch # User nenolod # Date 1162278153 28800 # Node ID fd9c0a5871ac3de8cd5a98ac13cc7d3e1800030f # Parent 9d9fc9e1de48ca43524feafb0b130b1639f501af [svn] - new and IMPROVED paranormal visualization studio diff -r 9d9fc9e1de48 -r fd9c0a5871ac ChangeLog --- a/ChangeLog Mon Oct 30 22:39:27 2006 -0800 +++ b/ChangeLog Mon Oct 30 23:02:33 2006 -0800 @@ -1,3 +1,11 @@ +2006-10-31 06:39:27 +0000 William Pitcock + revision [296] + - nuke paranormal (i will put a different paranormal back soon, no worries) + + trunk/configure.ac | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + + 2006-10-31 06:35:55 +0000 William Pitcock revision [294] - some tweaks diff -r 9d9fc9e1de48 -r fd9c0a5871ac configure.ac --- a/configure.ac Mon Oct 30 22:39:27 2006 -0800 +++ b/configure.ac Mon Oct 30 23:02:33 2006 -0800 @@ -1032,9 +1032,9 @@ have_xspf="no" ]) -#if test "$have_paranormal" = "yes"; then -# VISUALIZATION_PLUGINS="$VISUALIZATION_PLUGINS paranormal" -#fi +if test "$have_paranormal" = "yes"; then + VISUALIZATION_PLUGINS="$VISUALIZATION_PLUGINS paranormal" +fi if test "$have_xspf" = "yes"; then CONTAINER_PLUGINS="$CONTAINER_PLUGINS xspf" diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/Makefile Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,28 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +OBJECTIVE_LIBS = libparanormal$(SHARED_SUFFIX) + +LIBDIR = $(plugindir)/$(VISUALIZATION_PLUGIN_DIR) + +LIBADD = $(GTK_LIBS) $(XML_LIBS) $(SDL_LIBS) + +SOURCES = \ + actuators.c \ + builtins.c \ + cfg.c \ + cmaps.c \ + containers.c \ + freq.c \ + general.c \ + paranormal.c \ + plugin.c \ + presets.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 diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/actuators.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/actuators.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,177 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#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: + 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)) + 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; 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, + &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); +} + diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/actuators.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/actuators.h Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,98 @@ +/* FIXME: rename actuators to pn_actuators */ +/* FIXME: add a color type to the OPT_TYPE's */ + +#ifndef _ACTUATORS_H +#define _ACTUATORS_H + +#include +#include + +/* 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; + 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 */ diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/builtins.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/builtins.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,68 @@ +#ifdef HAVE_CONFIG_H +# include +#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); + +/* **************** cmaps **************** */ + +DECLARE_ACTUATOR (cmap_bwgradient); +DECLARE_ACTUATOR (cmap_gradient); + +/* **************** freq **************** */ +DECLARE_ACTUATOR (freq_dots); +DECLARE_ACTUATOR (freq_drops); + +/* **************** general **************** */ +DECLARE_ACTUATOR (general_fade); +DECLARE_ACTUATOR (general_blur); + +/* **************** wave **************** */ +DECLARE_ACTUATOR (wave_horizontal); +DECLARE_ACTUATOR (wave_vertical); +DECLARE_ACTUATOR (wave_normalize); +DECLARE_ACTUATOR (wave_smooth); +DECLARE_ACTUATOR (wave_radial); + +/* **************** xform **************** */ +DECLARE_ACTUATOR (xform_spin); +DECLARE_ACTUATOR (xform_ripple); +DECLARE_ACTUATOR (xform_bump_spin); + +/* **************** builtin_table **************** */ +struct pn_actuator_desc *builtin_table[] = +{ + /* **************** containers **************** */ + &builtin_container_simple, + &builtin_container_once, + &builtin_container_cycle, + /* **************** cmaps **************** */ + &builtin_cmap_bwgradient, + &builtin_cmap_gradient, + /* **************** freq **************** */ + &builtin_freq_dots, + &builtin_freq_drops, + /* **************** general **************** */ + &builtin_general_fade, + &builtin_general_blur, + /* **************** wave **************** */ + &builtin_wave_horizontal, + &builtin_wave_vertical, + &builtin_wave_normalize, + &builtin_wave_smooth, + &builtin_wave_radial, + /* **************** xform **************** */ + &builtin_xform_spin, + &builtin_xform_ripple, + &builtin_xform_bump_spin, + /* **************** the end! **************** */ + NULL +}; diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/cfg.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/cfg.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,625 @@ +/* FIXME: prevent the user from dragging something above the root + actuator */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "paranormal.h" +#include "actuators.h" +#include "containers.h" +#include "presets.h" + +/* DON'T CALL pn_fatal_error () IN HERE!!! */ + +/* Actuator page stuffs */ +static GtkWidget *cfg_dialog, *actuator_tree, *option_frame, *actuator_option_table; +static GtkWidget *actuator_add_opmenu, *actuator_add_button, *actuator_remove_button; +static GtkCTreeNode *selected_actuator_node; +static GtkTooltips *actuator_tooltips; + +/* This is used so that actuator_row_data_destroyed_cb won't free + the actuator associated w/ the node since we're going to be using it */ +gboolean destroy_row_data = TRUE; + +static void +actuator_row_data_destroyed_cb (struct pn_actuator *a) +{ + if (a && destroy_row_data) + destroy_actuator (a); +} + +static void +add_actuator (struct pn_actuator *a, GtkCTreeNode *parent, gboolean copy) +{ + GtkCTreeNode *node; + GSList *l; + + g_assert (cfg_dialog); + g_assert (actuator_tree); + g_assert (actuator_option_table); + + node = gtk_ctree_insert_node (GTK_CTREE (actuator_tree), parent, + NULL, (gchar**)&a->desc->name, 0, + NULL, NULL, NULL, NULL, + a->desc->flags & ACTUATOR_FLAG_CONTAINER + ? FALSE : TRUE, + TRUE); + + if (a->desc->flags & ACTUATOR_FLAG_CONTAINER) + for (l=*(GSList **)a->data; l; l = l->next) + { + add_actuator (l->data, node, copy); + } + + if (copy) + a = copy_actuator (a); + else if (a->desc->flags & ACTUATOR_FLAG_CONTAINER) + container_unlink_actuators (a); + + gtk_ctree_node_set_row_data_full (GTK_CTREE (actuator_tree), node, a, + ((GtkDestroyNotify) actuator_row_data_destroyed_cb)); +} + +static void +int_changed_cb (GtkSpinButton *sb, int *i) +{ + *i = gtk_spin_button_get_value_as_int (sb); +} + +static void +float_changed_cb (GtkSpinButton *sb, float *f) +{ + *f = gtk_spin_button_get_value_as_float (sb); +} + +static void +string_changed_cb (GtkEditable *t, char **s) +{ + if (*s != gtk_object_get_data (GTK_OBJECT (t), "DEFAULT_OP_STRING")) + g_free (*s); + + *s = gtk_editable_get_chars (t, 0, -1); +} + +static void +color_changed_cb (GtkSpinButton *sb, guchar *c) +{ + *c = gtk_spin_button_get_value_as_int (sb); +} + +static void +boolean_changed_cb (GtkToggleButton *tb, gboolean *b) +{ + *b = gtk_toggle_button_get_active (tb); +} + +static void +row_select_cb (GtkCTree *ctree, GtkCTreeNode *node, + gint column, gpointer data) +{ + struct pn_actuator *a; + int opt_count = 0, i, j; + GtkWidget *w; + GtkObject *adj; + + a = (struct pn_actuator *)gtk_ctree_node_get_row_data (ctree, node); + + /* count the actuator's options (plus one) */ + if (a->desc->option_descs) + while (a->desc->option_descs[opt_count++].name); + else + opt_count = 1; + + gtk_table_resize (GTK_TABLE (actuator_option_table), opt_count, 2); + + /* Actuator name */ + gtk_frame_set_label (GTK_FRAME (option_frame), a->desc->name); + + /* Actuator description */ + w = gtk_label_new (a->desc->doc); + gtk_label_set_line_wrap (GTK_LABEL (w), TRUE); + gtk_label_set_justify (GTK_LABEL (w), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (w), 0, .5); + gtk_widget_show (w); + gtk_table_attach (GTK_TABLE (actuator_option_table), w, 0, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, + 3, 3); + + /* now add the options */ + for (i=1, j=0; idesc->option_descs[j].name); + gtk_widget_show (w); + gtk_table_attach (GTK_TABLE (actuator_option_table), w, + 0, 1, i, i+1, + GTK_SHRINK | GTK_FILL, 0, + 3, 3); + switch (a->desc->option_descs[j].type) + { + case OPT_TYPE_INT: + adj = gtk_adjustment_new (a->options[j].val.ival, + G_MININT, G_MAXINT, + 1, 2, 0); + w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 0); + gtk_signal_connect (GTK_OBJECT (w), "changed", + GTK_SIGNAL_FUNC (int_changed_cb), + &a->options[j].val.ival); + break; + case OPT_TYPE_FLOAT: + adj = gtk_adjustment_new (a->options[j].val.fval, + -G_MAXFLOAT, G_MAXFLOAT, + 1, 2, 0); + w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 5); + gtk_signal_connect (GTK_OBJECT (w), "changed", + GTK_SIGNAL_FUNC (float_changed_cb), + &a->options[j].val.fval); + break; + case OPT_TYPE_STRING: + w = gtk_entry_new (); + gtk_widget_show (w); + gtk_entry_set_text (GTK_ENTRY (w), a->options[j].val.sval); + gtk_object_set_data (GTK_OBJECT (w), "DEFAULT_OP_STRING", + (gpointer)a->desc->option_descs[j].default_val.sval); + gtk_signal_connect (GTK_OBJECT (w), "changed", + GTK_SIGNAL_FUNC (string_changed_cb), + &a->options[j].val.sval); + break; + case OPT_TYPE_COLOR: + { + /* FIXME: add some color preview */ + GtkWidget *hbox; + hbox = gtk_hbox_new (FALSE, 0); + adj = gtk_adjustment_new (a->options[j].val.cval.r, + 0, 255, + 1, 2, 0); + w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 0); + gtk_widget_show (w); + gtk_signal_connect (GTK_OBJECT (w), "changed", + GTK_SIGNAL_FUNC (color_changed_cb), + &a->options[j].val.cval.r); + gtk_tooltips_set_tip (actuator_tooltips, w, + a->desc->option_descs[j].doc, NULL); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + adj = gtk_adjustment_new (a->options[j].val.cval.g, + 0, 255, + 1, 2, 0); + w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 0); + gtk_widget_show (w); + gtk_signal_connect (GTK_OBJECT (w), "changed", + GTK_SIGNAL_FUNC (color_changed_cb), + &a->options[j].val.cval.g); + gtk_tooltips_set_tip (actuator_tooltips, w, + a->desc->option_descs[j].doc, NULL); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 6); + adj = gtk_adjustment_new (a->options[j].val.cval.b, + 0, 255, + 1, 2, 0); + w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 0); + gtk_widget_show (w); + gtk_signal_connect (GTK_OBJECT (w), "changed", + GTK_SIGNAL_FUNC (color_changed_cb), + &a->options[j].val.cval.b); + gtk_tooltips_set_tip (actuator_tooltips, w, + a->desc->option_descs[j].doc, NULL); + gtk_box_pack_start (GTK_BOX (hbox), w, TRUE, TRUE, 0); + w = hbox; + } + break; + case OPT_TYPE_COLOR_INDEX: + adj = gtk_adjustment_new (a->options[j].val.ival, + 0, 255, + 1, 2, 0); + w = gtk_spin_button_new (GTK_ADJUSTMENT (adj), 1.0, 0); + gtk_signal_connect (GTK_OBJECT (w), "changed", + GTK_SIGNAL_FUNC (int_changed_cb), + &a->options[j].val.ival); + break; + case OPT_TYPE_BOOLEAN: + w = gtk_check_button_new (); + gtk_widget_show (w); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), + a->options[j].val.bval); + gtk_signal_connect (GTK_OBJECT (w), "clicked", + GTK_SIGNAL_FUNC (boolean_changed_cb), + &a->options[j].val.bval); + break; + } + gtk_widget_show (w); + gtk_tooltips_set_tip (actuator_tooltips, w, + a->desc->option_descs[j].doc, NULL); + gtk_table_attach (GTK_TABLE (actuator_option_table), w, + 1, 2, i, i+1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, + 0, + 3, 3); + } + + gtk_widget_set_sensitive (actuator_remove_button, TRUE); + gtk_widget_set_sensitive (actuator_add_button, a->desc->flags & ACTUATOR_FLAG_CONTAINER + ? TRUE : FALSE); + + selected_actuator_node = node; +} + +static void +table_remove_all_cb (GtkWidget *widget, gpointer data) +{ + gtk_container_remove (GTK_CONTAINER (actuator_option_table), + widget); +} + +static void +row_unselect_cb (GtkCTree *ctree, GList *node, gint column, + gpointer user_data) +{ + gtk_frame_set_label (GTK_FRAME (option_frame), NULL); + + gtk_container_foreach (GTK_CONTAINER (actuator_option_table), + table_remove_all_cb, NULL); + + /* Can't remove something if nothing's selected */ + gtk_widget_set_sensitive (actuator_remove_button, FALSE); + + selected_actuator_node = NULL; +} + +static void +add_actuator_cb (GtkButton *button, gpointer data) +{ + char *actuator_name; + struct pn_actuator *a; + + gtk_label_get (GTK_LABEL (GTK_BIN (actuator_add_opmenu)->child), + &actuator_name); + + a = create_actuator (actuator_name); + g_assert (a); + + add_actuator (a, selected_actuator_node, FALSE); +} + +static void +remove_actuator_cb (GtkButton *button, gpointer data) +{ + if (selected_actuator_node) + gtk_ctree_remove_node (GTK_CTREE (actuator_tree), + selected_actuator_node); +} + +/* Connect a node to its parent and replace the row data with + a copy of the node */ +static void +connect_actuators_cb (GtkCTree *ctree, GtkCTreeNode *node, + struct pn_actuator **root_ptr) +{ + struct pn_actuator *actuator, *parent, *copy; + + actuator = (struct pn_actuator *) gtk_ctree_node_get_row_data (ctree, node); + if (GTK_CTREE_ROW (node)->parent) + { + /* Connect it to the parent */ + parent = (struct pn_actuator *) + gtk_ctree_node_get_row_data (ctree, GTK_CTREE_ROW (node)->parent); + container_add_actuator (parent, actuator); + } + else + /* This is the root node; still gotta copy it, but we need to + save the original to *root_ptr */ + *root_ptr = actuator; + + /* we don't want our copy getting destroyed */ + destroy_row_data = FALSE; + + copy = copy_actuator (actuator); + gtk_ctree_node_set_row_data_full (ctree, node, copy, + ((GtkDestroyNotify)actuator_row_data_destroyed_cb)); + + /* Ok, now you can destroy it */ + destroy_row_data = TRUE; +} + +/* Extract (and connect) the actuators in the tree */ +static struct pn_actuator * +extract_actuator (void) +{ + GtkCTreeNode *root, *selected; + struct pn_actuator *root_actuator = NULL; + + root = gtk_ctree_node_nth (GTK_CTREE (actuator_tree), 0); + if (root) + gtk_ctree_post_recursive (GTK_CTREE (actuator_tree), root, + GTK_CTREE_FUNC (connect_actuators_cb), + &root_actuator); + + if (selected_actuator_node) + { + selected = selected_actuator_node; + gtk_ctree_unselect (GTK_CTREE (actuator_tree), GTK_CTREE_NODE (selected)); + gtk_ctree_select (GTK_CTREE (actuator_tree), GTK_CTREE_NODE (selected)); + } + + return root_actuator; +} + +/* If selector != NULL, then it's 'OK', otherwise it's 'Cancel' */ +static void +load_sel_cb (GtkButton *button, GtkFileSelection *selector) +{ + if (selector) + { + char *fname; + struct pn_actuator *a; + GtkCTreeNode *root; + + fname = gtk_file_selection_get_filename (selector); + a = load_preset (fname); + if (! a) + pn_error ("Unable to load file: \"%s\"", fname); + else + { + if ((root = gtk_ctree_node_nth (GTK_CTREE (actuator_tree), 0))) + gtk_ctree_remove_node (GTK_CTREE (actuator_tree), root); + add_actuator (a, NULL, FALSE); + } + } + + gtk_widget_set_sensitive (cfg_dialog, TRUE); +} + +static void +load_button_cb (GtkButton *button, gpointer data) +{ + GtkWidget *selector; + + selector = gtk_file_selection_new ("Load Preset"); + + gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button), + "clicked", GTK_SIGNAL_FUNC (load_sel_cb), selector); + gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button), + "clicked", GTK_SIGNAL_FUNC (load_sel_cb), NULL); + + gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + (gpointer) selector); + gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + (gpointer) selector); + + gtk_widget_set_sensitive (cfg_dialog, FALSE); + gtk_widget_show (selector); +} + +static void +save_sel_cb (GtkButton *button, GtkFileSelection *selector) +{ + if (selector) + { + char *fname; + struct pn_actuator *a; + + fname = gtk_file_selection_get_filename (selector); + a = extract_actuator (); + + if (! save_preset (fname, a)) + pn_error ("unable to save preset to file: %s", fname); + } + + gtk_widget_set_sensitive (cfg_dialog, TRUE); +} + +static void +save_button_cb (GtkButton *button, gpointer data) +{ + GtkWidget *selector; + + selector = gtk_file_selection_new ("Save Preset"); + + gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button), + "clicked", GTK_SIGNAL_FUNC (save_sel_cb), selector); + gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button), + "clicked", GTK_SIGNAL_FUNC (save_sel_cb), NULL); + + gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->ok_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + (gpointer) selector); + gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (selector)->cancel_button), + "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), + (gpointer) selector); + + gtk_widget_set_sensitive (cfg_dialog, FALSE); + gtk_widget_show (selector); +} + +static void +apply_settings (void) +{ + struct pn_rc rc; + + rc.actuator = extract_actuator (); + + pn_set_rc (&rc); +} + +static void +apply_button_cb (GtkButton *button, gpointer data) +{ + apply_settings (); +} + +static void +ok_button_cb (GtkButton *button, gpointer data) +{ + apply_settings (); + gtk_widget_hide (cfg_dialog); +} + +static void +cancel_button_cb (GtkButton *button, gpointer data) +{ + gtk_widget_destroy (cfg_dialog); + cfg_dialog = NULL; +} + +void +pn_configure (void) +{ + GtkWidget *notebook, *label, *scrollwindow, *menu, *menuitem; + GtkWidget *paned, *vbox, *table, *bbox, *button; + int i; + + + if (! cfg_dialog) + { + /* The dialog */ + cfg_dialog = gtk_dialog_new (); + gtk_window_set_title (GTK_WINDOW (cfg_dialog), "Configuration - " + PACKAGE " " VERSION); + gtk_widget_set_usize (cfg_dialog, 530, 370); + gtk_container_border_width (GTK_CONTAINER (cfg_dialog), 8); + gtk_signal_connect_object (GTK_OBJECT (cfg_dialog), "delete-event", + GTK_SIGNAL_FUNC (gtk_widget_hide), + GTK_OBJECT (cfg_dialog)); + + /* The notebook */ + notebook = gtk_notebook_new (); + gtk_widget_show (notebook); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cfg_dialog)->vbox), notebook, + TRUE, TRUE, 0); + + /* Actuator page */ + paned = gtk_hpaned_new (); + gtk_widget_show (paned); + label = gtk_label_new ("Actuators"); + gtk_widget_show (label); + gtk_notebook_append_page (GTK_NOTEBOOK (notebook), paned, label); + vbox = gtk_vbox_new (FALSE, 3); + gtk_widget_show (vbox); + gtk_paned_pack1 (GTK_PANED (paned), vbox, TRUE, FALSE); + scrollwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrollwindow); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwindow), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_box_pack_start (GTK_BOX (vbox), scrollwindow, TRUE, TRUE, 3); + actuator_tree = gtk_ctree_new (1, 0); + gtk_widget_show (actuator_tree); + gtk_ctree_set_reorderable (GTK_CTREE (actuator_tree), TRUE); + gtk_signal_connect (GTK_OBJECT (actuator_tree), "tree-select-row", + GTK_SIGNAL_FUNC (row_select_cb), NULL); + gtk_signal_connect (GTK_OBJECT (actuator_tree), "tree-unselect-row", + GTK_SIGNAL_FUNC (row_unselect_cb), NULL); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwindow), + actuator_tree); + table = gtk_table_new (3, 2, TRUE); + gtk_widget_show (table); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 3); + actuator_add_opmenu = gtk_option_menu_new (); + gtk_widget_show (actuator_add_opmenu); + menu = gtk_menu_new (); + gtk_widget_show (menu); + for (i=0; builtin_table[i]; i++) + { + /* FIXME: Add actuator group support */ + menuitem = gtk_menu_item_new_with_label (builtin_table[i]->name); + gtk_widget_show (menuitem); + gtk_menu_append (GTK_MENU (menu), menuitem); + } + gtk_option_menu_set_menu (GTK_OPTION_MENU (actuator_add_opmenu), menu); + gtk_table_attach (GTK_TABLE (table), actuator_add_opmenu, + 0, 2, 0, 1, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, + 3, 3); + actuator_add_button = gtk_button_new_with_label ("Add"); + gtk_widget_show (actuator_add_button); + gtk_signal_connect (GTK_OBJECT (actuator_add_button), "clicked", + GTK_SIGNAL_FUNC (add_actuator_cb), NULL); + gtk_table_attach (GTK_TABLE (table), actuator_add_button, + 0, 1, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, + 3, 3); + actuator_remove_button = gtk_button_new_with_label ("Remove"); + gtk_widget_set_sensitive (actuator_remove_button, FALSE); + gtk_widget_show (actuator_remove_button); + gtk_signal_connect (GTK_OBJECT (actuator_remove_button), "clicked", + GTK_SIGNAL_FUNC (remove_actuator_cb), NULL); + gtk_table_attach (GTK_TABLE (table), actuator_remove_button, + 1, 2, 1, 2, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, + 3, 3); + button = gtk_button_new_with_label ("Load"); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (load_button_cb), NULL); + gtk_table_attach (GTK_TABLE (table), button, + 0, 1, 2, 3, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, + 3, 3); + button = gtk_button_new_with_label ("Save"); + gtk_widget_show (button); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (save_button_cb), NULL); + gtk_table_attach (GTK_TABLE (table), button, + 1, 2, 2, 3, + GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, + 3, 3); + + /* Option table */ + option_frame = gtk_frame_new (NULL); + gtk_widget_show (option_frame); + gtk_container_set_border_width (GTK_CONTAINER (option_frame), 3); + gtk_paned_pack2 (GTK_PANED (paned), option_frame, TRUE, TRUE); + scrollwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (scrollwindow); + gtk_container_set_border_width (GTK_CONTAINER (scrollwindow), 3); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwindow), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (option_frame), scrollwindow); + actuator_option_table = gtk_table_new (0, 2, FALSE); + gtk_widget_show (actuator_option_table); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrollwindow), + actuator_option_table); + gtk_paned_set_position (GTK_PANED (paned), 0); + actuator_tooltips = gtk_tooltips_new (); + gtk_tooltips_enable (actuator_tooltips); + + /* Build the initial actuator actuator_tree */ + if (pn_rc->actuator) + { + add_actuator (pn_rc->actuator, NULL, TRUE); + gtk_widget_set_sensitive (actuator_add_button, FALSE); + } + + /* OK / Apply / Cancel */ + bbox = gtk_hbutton_box_new (); + gtk_widget_show (bbox); + gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing (GTK_BUTTON_BOX (bbox), 8); + gtk_button_box_set_child_size (GTK_BUTTON_BOX (bbox), 64, 0); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (cfg_dialog)->action_area), + bbox, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("OK"); + gtk_widget_show (button); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (ok_button_cb), NULL); + gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("Apply"); + gtk_widget_show (button); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (apply_button_cb), NULL); + gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0); + button = gtk_button_new_with_label ("Cancel"); + gtk_widget_show (button); + gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NORMAL); + gtk_signal_connect (GTK_OBJECT (button), "clicked", + GTK_SIGNAL_FUNC (cancel_button_cb), NULL); + gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0); + } + + gtk_widget_show (cfg_dialog); + gtk_widget_grab_focus (cfg_dialog); +} diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/cmaps.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/cmaps.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,93 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "paranormal.h" +#include "actuators.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} } }, + { 0 } +}; + +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", + "Sets the colormap to a gradient going from to " + "", + 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} } }, + { 0 } +}; + +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", + "Sets the colormap to a gradient going from black to " + "while, via an intermediate color", + 0, cmap_bwgradient_opts, + NULL, NULL, cmap_bwgradient_exec +}; diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/containers.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/containers.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,202 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +#include "actuators.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", + "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", + "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 } }, + { "random", "Whether or not the change should be random", + OPT_TYPE_BOOLEAN, { bval: TRUE } }, + { 0 } +}; + +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; + + 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", + "A container that alternates which of its children is executed; it " + "can either change children randomly or go in order.", + ACTUATOR_FLAG_CONTAINER, container_cycle_opts, + container_cycle_init, container_cycle_cleanup, container_cycle_exec +}; diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/containers.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/containers.h Mon Oct 30 23:02:33 2006 -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 */ diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/freq.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/freq.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,62 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include + +#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", + "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; jfreq_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", + "Draws dots at random on the image (louder music = more dots)", + 0, NULL, + NULL, NULL, freq_drops_exec +}; diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/general.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/general.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,96 @@ +/* FIXME: what to name this file? */ + +#ifdef HAVE_CONFIG_H +# include +#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 } }, + { 0 } +}; + +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; jheight; j++) + for (i=0; iwidth; 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", "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; jheight; j++) + for (i=0; iwidth; 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", "A simple 1 pixel radius blur", + 0, NULL, + NULL, NULL, general_blur_exec +}; diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/paranormal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/paranormal.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,205 @@ +/* FIXME: add fullscreen / screenshots */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include + +#include "paranormal.h" +#include "actuators.h" + +/* SDL stuffs */ +static 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]; + +/* **************** 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); + + for (j=0; jheight; 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; + + pn_sound_data = g_new0 (struct pn_sound_data, 1); + pn_image_data = g_new0 (struct pn_image_data, 1); + + if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_NOPARACHUTE) < 0) + pn_fatal_error ("Unable to initialize SDL: %s", SDL_GetError ()); + + resize_video (640, 360); + + SDL_WM_SetCaption (PACKAGE " " 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; + } + } + + if (pn_rc->actuator) + { + exec_actuator (pn_rc->actuator); + blit_to_screen (); + } +} + +/* 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; +} + + + + + + + + + + diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/paranormal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/paranormal.h Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,53 @@ +#ifndef _PARANORMAL_H +#define _PARANORMAL_H + +#include + +#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; + +/* global trig pre-computes */ +extern float sin_val[360]; +extern float cos_val[360]; + +#endif /* _PARANORMAL_H */ diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/plugin.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/plugin.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,478 @@ +/* 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 +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "paranormal.h" +#include "actuators.h" +#include "presets.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; +static 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, + "Paranormal Visualization Studio " 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 = create_actuator ("cmap_bwgradient"); + if (! b) goto ugh; + b->options[2].val.cval.r = b->options[2].val.cval.g = 0; + container_add_actuator (a, b); + container_add_actuator (pn_rc->actuator, a); + a = create_actuator ("general_fade"); + if (! a) goto ugh; + container_add_actuator (pn_rc->actuator, a); + a = create_actuator ("xform_spin"); + if (! a) goto ugh; + a->options[0].val.fval = -4.0; + a->options[2].val.fval = 0.9; + container_add_actuator (pn_rc->actuator, a); + a = create_actuator ("wave_horizontal"); + if (! a) goto ugh; + container_add_actuator (pn_rc->actuator, a); + a = create_actuator ("general_blur"); + 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) +{ + 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); + } + + /* 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 */ + 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) +{ + static GtkWidget *window=NULL; + GtkWidget *vbox, *buttonbox, *close, *label; + + if(window) + return; + + window=gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), "Paranormal Visualization Studio " VERSION); + gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE); + + vbox=gtk_vbox_new(FALSE, 4); + gtk_container_add(GTK_CONTAINER(window), vbox); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 8); + gtk_widget_show(vbox); + + label=gtk_label_new("\n\n Paranormal Visualization Studio " " VERSION "\n\n\ +Copyright (C) 2006, William Pitcock. \n\ +Copyright (C) 2001, Jamie Gennis. (jgennis@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"); + + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 8); + gtk_widget_show(label); + + buttonbox=gtk_hbutton_box_new(); + gtk_box_pack_end(GTK_BOX(vbox), buttonbox, FALSE, FALSE,8); + gtk_widget_show(buttonbox); + + close=gtk_button_new_with_label("Close"); + GTK_WIDGET_SET_FLAGS(close, GTK_CAN_DEFAULT); + gtk_window_set_default(GTK_WINDOW(window), close); + gtk_hbutton_box_set_layout_default(GTK_BUTTONBOX_END); + gtk_box_pack_end(GTK_BOX(buttonbox), close, FALSE, FALSE,8); + gtk_widget_show(close); + + gtk_signal_connect(GTK_OBJECT(close), "clicked", GTK_SIGNAL_FUNC(about_close_clicked), &window); + gtk_signal_connect(GTK_OBJECT(window), "delete-event", GTK_SIGNAL_FUNC(about_closed), &window); + + gtk_widget_show(window); +} + +static void +pn_xmms_configure (void) +{ + load_pn_rc (); + + /* We should already have a GDK_THREADS_ENTER + but we need to give it config_mutex */ + if (config_mutex) + SDL_mutexP (config_mutex); + + pn_configure (); + + if (config_mutex) + SDL_mutexV (config_mutex); +} + +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->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 - " PACKAGE " " 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, PACKAGE ": %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 - " PACKAGE " " 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 (); + } +} diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/pn_utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/pn_utils.h Mon Oct 30 23:02:33 2006 -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 */ diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/presets.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/presets.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,261 @@ +/* FIXME: add documentation support to preset files */ +/* FIXME: add multiple-presets-per-file support (maybe) */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include "paranormal.h" +#include "actuators.h" +#include "containers.h" + +/* cur->name should be the actuator name */ +static void +parse_actuator (xmlNodePtr cur, struct pn_actuator *a) +{ + int i; + xmlChar *content; + struct pn_actuator *child; + + for (cur = cur->xmlChildrenNode; cur; cur = cur->next) + { + if (xmlIsBlankNode (cur) || cur->type != XML_ELEMENT_NODE) + continue; + + /* see if it's an option */ + for (i=0; a->options && a->options[i].desc; i++) + if (! xmlStrcmp (cur->name, + (const xmlChar *) a->options[i].desc->name)) + break; + + if (a->options && a->options[i].desc) + { + /* it is an option, so let's set it! */ + content = xmlNodeGetContent (cur); + + /* FIXME: warning? */ + if (! content) + continue; + + /* FIXME: perhaps do a little better job of error checking? */ + switch (a->options[i].desc->type) + { + case OPT_TYPE_INT: + a->options[i].val.ival = (int)strtol (content, NULL, 0); + break; + case OPT_TYPE_FLOAT: + a->options[i].val.fval = (float)strtod (content, NULL); + break; + case OPT_TYPE_STRING: + a->options[i].val.sval = g_strdup (content); + break; + case OPT_TYPE_COLOR: + { + guint r,g,b; + char *s = content+1; + r = strtoul (s, &s, 0); + if (r > 255 || ! (s = strchr (s, ','))) + goto bad_color; + g = strtoul (s+1, &s, 0); + if (g > 255 || ! (s = strchr (s, ','))) + goto bad_color; + b = strtoul (s+1, NULL, 0); + if (b > 255) + goto bad_color; + + a->options[i].val.cval.r = (guchar)r; + a->options[i].val.cval.g = (guchar)g; + a->options[i].val.cval.b = (guchar)b; + + break; + } + bad_color: + pn_error ("parse error: invalid color value: option \"%s\" ignored.\n" + " correct syntax: (r,g,b) where r, g, and b are the\n" + " red, green, and blue components of the " + "color, respectively", cur->name); + break; + + case OPT_TYPE_COLOR_INDEX: + { + int c = (int)strtol (content, NULL, 0); + if (c < 0 || c > 255) + pn_error ("parse error: invalid color index \"%s\" (%d): option ignored.\n" + " the value must be between 0 and 255", + cur->name, c); + else + a->options[i].val.ival = c; + break; + } + case OPT_TYPE_BOOLEAN: + { + char *c, *d; + for (c=content; isspace (*c); c++); + for (d=c; !isspace(*d); d++); + *d = '\0'; + if (g_strcasecmp (c, "true") == 0) + a->options[i].val.bval = TRUE; + else if (g_strcasecmp (c, "false") == 0) + a->options[i].val.bval = FALSE; + else + pn_error ("parse error: invalid boolean value \"%s\" (%s): option ignored.\n" + " the value must be either 'true' or 'false'", + cur->name, c); + } + } + + /* gotta free content */ + xmlFree (content); + } + /* See if we have a child actuator */ + else if (a->desc->flags & ACTUATOR_FLAG_CONTAINER + && (child = create_actuator (cur->name))) + { + container_add_actuator (a, child); + parse_actuator (cur, child); + } + else + /* We have an error */ + pn_error ("parse error: unknown entity \"%s\": ignored.", cur->name); + } +} + +struct pn_actuator * +load_preset (const char *filename) +{ + xmlDocPtr doc; + xmlNodePtr cur; + struct pn_actuator *a = NULL; + + doc = xmlParseFile (filename); + if (! doc) + return NULL; + + cur = xmlDocGetRootElement (doc); + if (! cur) + xmlFreeDoc (doc); + + if (xmlStrcmp (cur->name, (const xmlChar *) "paranormal_preset")) + { + xmlFreeDoc (doc); + return NULL; + } + + for (cur = cur->children; cur; cur = cur->next) + { + if (xmlIsBlankNode (cur) || cur->type != XML_ELEMENT_NODE) + continue; + + /* if (...) { ... } else if (is_documentation [see top of file]) ... else */ + { + a = create_actuator (cur->name); + + /* FIXME: warn? */ + if (! a) + continue; + + parse_actuator (cur, a); + break; + } + } + + /* Don't need this any longer */ + xmlFreeDoc (doc); + + return a; +} + +/* FIXME: do the file writing w/ error checking */ +static gboolean +save_preset_recursive (FILE *file, const struct pn_actuator *actuator, + int recursion_depth) +{ + int i; + GSList *child; + + /* open this actuator */ + fprintf (file, "%*s<%s>\n", recursion_depth, "", actuator->desc->name); + + /* options */ + if (actuator->options) + for (i=0; actuator->options[i].desc; i++) + { + fprintf (file, "%*s <%s> ", recursion_depth, "", + actuator->desc->option_descs[i].name); + switch (actuator->desc->option_descs[i].type) + { + case OPT_TYPE_INT: + case OPT_TYPE_COLOR_INDEX: + fprintf (file, "%d", actuator->options[i].val.ival); + break; + case OPT_TYPE_FLOAT: + fprintf (file, "%.5f", actuator->options[i].val.fval); + break; + case OPT_TYPE_STRING: + fprintf (file, "%s", actuator->options[i].val.sval); + break; + case OPT_TYPE_COLOR: + fprintf (file, "%d, %d, %d", actuator->options[i].val.cval.r, + actuator->options[i].val.cval.g, + actuator->options[i].val.cval.b); + break; + case OPT_TYPE_BOOLEAN: + if (actuator->options[i].val.bval) + fprintf (file, "TRUE"); + else + fprintf (file, "FALSE"); + break; + } + fprintf (file, " \n", actuator->desc->option_descs[i].name); + } + + /* children */ + if (actuator->desc->flags & ACTUATOR_FLAG_CONTAINER) + for (child = *(GSList **)actuator->data; child; child = child->next) + if (! save_preset_recursive (file, (struct pn_actuator*) child->data, + recursion_depth+1)) + return FALSE; + + /* close the actuator */ + fprintf (file, "%*s\n", recursion_depth, "", actuator->desc->name); + + return TRUE; +} + +gboolean +save_preset (const char *filename, const struct pn_actuator *actuator) +{ + FILE *file; + + file = fopen (filename, "w"); + if (! file) + { + pn_error ("fopen: %s", strerror (errno)); + return FALSE; + } + + fprintf (file, "\n\n\n"); + + if (actuator) + if (! save_preset_recursive (file, actuator, 1)) + { + fclose (file); + return FALSE; + } + + fprintf (file, ""); + + fclose (file); + + return TRUE; + +} diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/presets.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/presets.h Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,11 @@ +#ifndef _PRESETS_H +#define _PRESETS_H + +#include + +#include "actuators.h" + +struct pn_actuator *load_preset (const char *filename); +gboolean save_preset (const char *filename, const struct pn_actuator *actuator); + +#endif /* _PRESETS_H */ diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/wave.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/wave.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,248 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "paranormal.h" +#include "actuators.h" +#include "pn_utils.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} }, + { 0 } +}; + +static void +wave_horizontal_exec (const struct pn_actuator_option *opts, + gpointer data) +{ + int i; + int channel = ( opts[0].val.ival < 0 ) ? 0 : 1; + + for (i=0; iwidth; 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))] + = 0xff; + } + + /*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))] + = 0xff; + + 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))] + = 0xff; + } + } +} + +struct pn_actuator_desc builtin_wave_horizontal = +{ + "wave_horizontal", "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} }, + { 0 } +}; + +static void +wave_vertical_exec (const struct pn_actuator_option *opts, + gpointer data) +{ + int i; + int channel = ( opts[0].val.ival < 0 ) ? 0 : 1; + + for (i=0; iheight; 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)] + = 0xff; + } + 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)] + = 0xff; + 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)] + + = 0xff; + } + } +} + + +struct pn_actuator_desc builtin_wave_vertical = +{ + "wave_vertical", "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 } }, + { 0 } +}; + +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", "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 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 } }, + { 0 } +}; + +static void +wave_radial_exec (const struct pn_actuator_option *opts, + gpointer data) +{ + int i, x, y; + + 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))] + = 0xff; + } +}; + +struct pn_actuator_desc builtin_wave_radial = +{ + "wave_radial", "Draws a single waveform varying" + " radially from the center of the image", + 0, wave_radial_opts, + NULL, NULL, wave_radial_exec +}; + + + diff -r 9d9fc9e1de48 -r fd9c0a5871ac src/paranormal/xform.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/paranormal/xform.c Mon Oct 30 23:02:33 2006 -0800 @@ -0,0 +1,383 @@ +/* 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 +#endif + +#include + +#include + +#include "paranormal.h" +#include "actuators.h" +#include "pn_utils.h" + +struct xform_vector +{ + guint 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; + + for (i=0, v=vfield, destptr=pn_image_data->surface[1]; + iwidth*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 } }, + { 0 } +}; + +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_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); iwidth>>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", "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 } }, + { 0 } +}; + +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); iwidth>>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", "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 } }, + { 0 } +}; + +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); iwidth>>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", "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 +}; +