aboutsummaryrefslogtreecommitdiff
path: root/cyclone/hammer/pv.c
diff options
context:
space:
mode:
Diffstat (limited to 'cyclone/hammer/pv.c')
-rw-r--r--cyclone/hammer/pv.c457
1 files changed, 457 insertions, 0 deletions
diff --git a/cyclone/hammer/pv.c b/cyclone/hammer/pv.c
new file mode 100644
index 0000000..235da2c
--- /dev/null
+++ b/cyclone/hammer/pv.c
@@ -0,0 +1,457 @@
+/* Copyright (c) 2002-2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* CHECKED (it cannot be emulated): creating [s/r <pv-symbol>] prints
+ "error:send/receive:<pv-symbol>:already exists"
+*/
+
+#include <string.h>
+#include "m_pd.h"
+#include "g_canvas.h"
+#include "common/loud.h"
+#include "common/grow.h"
+
+#define PV_INISIZE 32 /* LATER rethink */
+#define PV_MAXSIZE 256
+
+typedef struct _pvfamily
+{
+ t_symbol *f_selector;
+ t_float f_float;
+ t_symbol *f_symbol;
+ t_gpointer *f_pointer;
+ int f_size; /* as allocated */
+ int f_natoms; /* as used */
+ t_atom *f_message;
+ t_atom f_messini[PV_INISIZE];
+ t_glist *f_glist; /* root glist of a family */
+ t_symbol *f_name;
+ struct _pvfamily *f_next;
+} t_pvfamily;
+
+typedef struct _pvlist
+{
+ t_pd l_pd;
+ int l_refcount;
+ t_symbol *l_name;
+ t_pvfamily *l_pvlist;
+} t_pvlist;
+
+typedef struct _pv
+{
+ t_object x_ob;
+ t_glist *x_glist;
+ t_symbol *x_name;
+ t_pvfamily *x_family;
+} t_pv;
+
+static t_class *pv_class;
+static t_class *pvlist_class;
+
+static void pvlist_decrement(t_pvlist *pl)
+{
+ if (!--pl->l_refcount)
+ {
+ pd_unbind(&pl->l_pd, pl->l_name);
+ pd_free(&pl->l_pd);
+ }
+}
+
+static t_pvlist *pv_getlist(t_symbol *s, int create)
+{
+ t_pvlist *pl = (t_pvlist *)pd_findbyclass(s, pvlist_class);
+ if (pl)
+ {
+ if (create) pl->l_refcount++;
+ }
+ else
+ {
+ if (create)
+ {
+ pl = (t_pvlist *)pd_new(pvlist_class);
+ pl->l_refcount = 1;
+ pl->l_name = s;
+ pl->l_pvlist = 0;
+ pd_bind(&pl->l_pd, s);
+ }
+ else bug("pv_getlist");
+ }
+ return (pl);
+}
+
+static t_pvfamily *pv_newfamily(t_pvlist *pvlist)
+{
+ t_pvfamily *pf = (t_pvfamily *)getbytes(sizeof(*pf));
+ pf->f_name = pvlist->l_name;
+ pf->f_next = pvlist->l_pvlist;
+ pvlist->l_pvlist = pf;
+ pf->f_selector = 0;
+ pf->f_float = 0;
+ pf->f_symbol = 0;
+ pf->f_pointer = 0;
+ pf->f_size = PV_INISIZE;
+ pf->f_natoms = 0;
+ pf->f_message = pf->f_messini;
+ return (pf);
+}
+
+static void pvfamily_free(t_pvfamily *pf)
+{
+ if (pf->f_message != pf->f_messini)
+ freebytes(pf->f_message, pf->f_size * sizeof(*pf->f_message));
+ freebytes(pf, sizeof(*pf));
+}
+
+static void pv_update(t_glist *glist, t_pvfamily *pf)
+{
+ t_gobj *g;
+ for (g = glist->gl_list; g; g = g->g_next)
+ if (pd_class(&g->g_pd) == canvas_class) /* LATER rethink */
+ pv_update((t_glist *)g, pf);
+ else if (pd_class(&g->g_pd) == pv_class
+ && ((t_pv *)g)->x_name == pf->f_name)
+ ((t_pv *)g)->x_family = pf;
+}
+
+static t_pvfamily *pvfamily_reusable;
+
+static void pv_breakup(t_pvlist *pvlist, t_glist *glist)
+{
+ t_gobj *g;
+ for (g = glist->gl_list; g; g = g->g_next)
+ {
+ if (pd_class(&g->g_pd) == pv_class
+ && ((t_pv *)g)->x_name == pvlist->l_name)
+ {
+ t_pvfamily *pf;
+ if (!(pf = pvfamily_reusable))
+ pf = pv_newfamily(pvlist);
+ else pvfamily_reusable = 0;
+ pf->f_glist = glist;
+ pv_update(glist, pf);
+ /* LATER keep current value */
+ return;
+ }
+ }
+ for (g = glist->gl_list; g; g = g->g_next)
+ if (pd_class(&g->g_pd) == canvas_class) /* LATER rethink */
+ pv_breakup(pvlist, (t_glist *)g);
+}
+
+/* join all families of a 'pvlist' rooted in any subglist of a 'glist' */
+static t_pvfamily *pv_joinup(t_pvlist *pvlist, t_glist *glist)
+{
+ t_pvfamily *result = 0;
+ t_pvfamily *pf, *pfprev, *pfnext;
+ for (pfprev = 0, pf = pvlist->l_pvlist; pf; pfprev = pf, pf = pfnext)
+ {
+ t_glist *gl;
+ pfnext = pf->f_next;
+ for (gl = pf->f_glist; gl; gl = gl->gl_owner)
+ {
+ if (gl == glist)
+ {
+ if (result)
+ {
+ pvfamily_free(pf);
+ if (pfprev)
+ pfprev->f_next = pfnext;
+ else
+ pvlist->l_pvlist = pfnext;
+ pf = pfprev;
+ }
+ else result = pf;
+ break;
+ }
+ }
+ }
+ return (result);
+}
+
+/* Called normally with either 'create' or 'destroy' set to 1,
+ but it might be called with both set to 0 for testing. */
+static t_pvfamily *pv_getfamily(t_glist *glist, t_symbol *s,
+ int create, int destroy)
+{
+ t_pvlist *pl = pv_getlist(s, create);
+ if (pl)
+ {
+ if (destroy)
+ {
+ t_pvfamily *pf, *mypf;
+ t_glist *gl;
+ for (mypf = pl->l_pvlist; mypf; mypf = mypf->f_next)
+ if (mypf->f_glist == glist)
+ break;
+ /* mypf is not null iff we are invoked via a [pv] in root */
+ /* now check if there is a family rooted in a super-branch */
+ for (gl = glist->gl_owner; gl; gl = gl->gl_owner)
+ {
+ for (pf = pl->l_pvlist; pf; pf = pf->f_next)
+ {
+ if (pf->f_glist == gl)
+ {
+ if (mypf)
+ bug("pv_getfamily 1: %s in %s",
+ mypf->f_name->s_name,
+ mypf->f_glist->gl_name->s_name);
+ else
+ return (0);
+ }
+ }
+ }
+ if (mypf)
+ {
+ pvfamily_reusable = mypf;
+ pv_breakup(pl, glist);
+ if (pvfamily_reusable == mypf)
+ {
+ pvfamily_reusable = 0;
+ if (pl->l_pvlist == mypf)
+ pl->l_pvlist = mypf->f_next;
+ else
+ {
+ for (pf = pl->l_pvlist; pf; pf = pf->f_next)
+ {
+ if (pf->f_next == mypf)
+ {
+ pf->f_next = mypf->f_next;
+ break;
+ }
+ }
+ if (!pf) bug("pv_getfamily 2");
+ }
+ pvfamily_free(mypf);
+ }
+ }
+ else bug("pv_getfamily 3");
+ pvlist_decrement(pl);
+ }
+ else
+ {
+ t_pvfamily *pf;
+ t_glist *gl;
+ for (gl = glist; gl; gl = gl->gl_owner)
+ for (pf = pl->l_pvlist; pf; pf = pf->f_next)
+ if (pf->f_glist == gl)
+ return (pf);
+ if (create)
+ {
+ if (!(pf = pv_joinup(pl, glist)))
+ pf = pv_newfamily(pl);
+ pf->f_glist = glist;
+ pv_update(glist, pf);
+ return (pf);
+ }
+ else bug("pv_getfamily 4");
+ }
+ }
+ else bug("pv_getfamily 5");
+ return (0);
+}
+
+static t_pvfamily *pv_checkfamily(t_pv *x)
+{
+ if (!x->x_family)
+ {
+ bug("pv_checkfamily");
+ x->x_family = pv_getfamily(x->x_glist, x->x_name, 0, 0);
+ }
+ return (x->x_family);
+}
+
+static void pv_bang(t_pv *x)
+{
+ t_pvfamily *pf = pv_checkfamily(x);
+ if (pf)
+ {
+ t_symbol *s = pf->f_selector;
+ if (s == &s_bang)
+ outlet_bang(((t_object *)x)->ob_outlet);
+ else if (s == &s_float)
+ outlet_float(((t_object *)x)->ob_outlet, pf->f_float);
+ else if (s == &s_symbol && pf->f_symbol)
+ outlet_symbol(((t_object *)x)->ob_outlet, pf->f_symbol);
+ else if (s == &s_pointer)
+ {
+ /* LATER */
+ }
+ else if (s == &s_list)
+ outlet_list(((t_object *)x)->ob_outlet,
+ s, pf->f_natoms, pf->f_message);
+ else if (s)
+ outlet_anything(((t_object *)x)->ob_outlet,
+ s, pf->f_natoms, pf->f_message);
+ }
+}
+
+static void pv_float(t_pv *x, t_float f)
+{
+ t_pvfamily *pf = pv_checkfamily(x);
+ if (pf)
+ {
+ pf->f_selector = &s_float;
+ pf->f_float = f;
+ pf->f_natoms = 0; /* defensive */
+ }
+}
+
+static void pv_symbol(t_pv *x, t_symbol *s)
+{
+ t_pvfamily *pf = pv_checkfamily(x);
+ if (pf)
+ {
+ pf->f_selector = &s_symbol;
+ pf->f_symbol = s;
+ pf->f_natoms = 0; /* defensive */
+ }
+}
+
+static void pv_pointer(t_pv *x, t_gpointer *gp)
+{
+ t_pvfamily *pf = pv_checkfamily(x);
+ if (pf)
+ {
+ pf->f_selector = &s_pointer;
+ pf->f_pointer = gp;
+ pf->f_natoms = 0; /* defensive */
+ }
+}
+
+static void pvfamily_domessage(t_pvfamily *pf, int ac, t_atom *av)
+{
+ if (ac > pf->f_size)
+ {
+ /* LATER consider using PV_MAXSIZE (and warning if exceeded) */
+ pf->f_message = grow_nodata(&ac, &pf->f_size, pf->f_message,
+ PV_INISIZE, pf->f_messini,
+ sizeof(*pf->f_message));
+ }
+ pf->f_natoms = ac;
+ memcpy(pf->f_message, av, ac * sizeof(*pf->f_message));
+}
+
+static void pv_list(t_pv *x, t_symbol *s, int ac, t_atom *av)
+{
+ t_pvfamily *pf = pv_checkfamily(x);
+ if (pf)
+ {
+ pf->f_selector = &s_list; /* LATER rethink */
+ pvfamily_domessage(pf, ac, av);
+ }
+}
+
+static void pv_anything(t_pv *x, t_symbol *s, int ac, t_atom *av)
+{
+ t_pvfamily *pf = pv_checkfamily(x);
+ if (pf)
+ {
+ pf->f_selector = s; /* LATER rethink */
+ pvfamily_domessage(pf, ac, av);
+ }
+}
+
+static void pv_objstatus(t_pv *x, t_glist *glist)
+{
+ t_gobj *g;
+ for (g = glist->gl_list; g; g = g->g_next)
+ {
+ if (g == (t_gobj *)x)
+ post("%x (this object) owning patcher [%s]",
+ (int)g, glist->gl_name->s_name);
+ else if (pd_class(&g->g_pd) == pv_class
+ && ((t_pv *)g)->x_name == x->x_name)
+ post("%x owning patcher [%s]", (int)g, glist->gl_name->s_name);
+ }
+}
+
+static void pv_status(t_pv *x)
+{
+ t_pvlist *pl = pv_getlist(x->x_name, 0);
+ post("pv status: Tied to %s", x->x_name->s_name);
+ if (pl)
+ {
+ t_pvfamily *pf;
+ int fcount;
+ for (pf = pl->l_pvlist, fcount = 1; pf; pf = pf->f_next, fcount++)
+ {
+ t_glist *glist = pf->f_glist;
+ t_gobj *g;
+ post("Family %d:", fcount);
+ pv_objstatus(x, glist);
+ for (g = glist->gl_list; g; g = g->g_next)
+ if (pd_class(&g->g_pd) == canvas_class) /* LATER rethink */
+ pv_objstatus(x, (t_glist *)g);
+ }
+ }
+}
+
+static void pv_free(t_pv *x)
+{
+ pv_getfamily(x->x_glist, x->x_name, 0, 1);
+}
+
+static void *pv_new(t_symbol *s, int ac, t_atom *av)
+{
+ t_pv *x = 0;
+ if (ac && av->a_type == A_SYMBOL)
+ s = av->a_w.w_symbol;
+ else s = 0;
+ if (s && s != &s_)
+ {
+ t_glist *gl = canvas_getcurrent();
+ t_pvfamily *pf = pv_getfamily(gl, s, 1, 0);
+ x = (t_pv *)pd_new(pv_class);
+ x->x_glist = gl;
+ x->x_name = s;
+ x->x_family = pf;
+ outlet_new((t_object *)x, &s_float);
+ if (--ac)
+ {
+ av++;
+ if (av->a_type == A_SYMBOL)
+ {
+ if (av->a_w.w_symbol == &s_symbol)
+ {
+ if (ac > 1 && av[1].a_type == A_SYMBOL)
+ pv_symbol(x, av[1].a_w.w_symbol);
+ }
+ /* LATER rethink 'pv <name> bang' (now it is accepted) */
+ else pv_anything(x, av->a_w.w_symbol, ac - 1, av + 1);
+ }
+ else if (av->a_type == A_FLOAT)
+ {
+ if (ac > 1)
+ pv_list(x, &s_list, ac, av);
+ else pv_float(x, av->a_w.w_float);
+ }
+ }
+
+ }
+ else
+ /* CHECKED: "error: missing or bad arguments",
+ a box is created without inlets and outlets */
+ loud_classarg(pv_class);
+ return (x);
+}
+
+void pv_setup(void)
+{
+ pv_class = class_new(gensym("pv"),
+ (t_newmethod)pv_new,
+ (t_method)pv_free,
+ sizeof(t_pv), 0, A_GIMME, 0);
+ class_addbang(pv_class, pv_bang);
+ class_addfloat(pv_class, pv_float);
+ class_addsymbol(pv_class, pv_symbol);
+ class_addpointer(pv_class, pv_pointer);
+ class_addlist(pv_class, pv_list);
+ class_addanything(pv_class, pv_anything);
+ class_addmethod(pv_class, (t_method)pv_status,
+ gensym("status"), 0);
+ /* CHECKED: sending bang (or int, list, status, etc.) with '; <pv-symbol>'
+ "error::doesn't understand bang" (class name is an empty string) */
+ pvlist_class = class_new(&s_, 0, 0,
+ sizeof(t_pvlist), CLASS_PD, 0);
+}