/* Copyright (c) 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. */ #include #include "m_pd.h" #include "common/loud.h" #include "plusbob.h" //#define PLUSBOB_DEBUG /* LATER let there be a choice of using either fake-symbols, or gpointers. The gpointer layout would be such: gs_un points to a plusbob-like structure (without the bob_tag field), a unique integer code has to be reserved for gs_which, the fields gp_un and gp_valid are ignored. Using bob_refcount instead of gs_refcount is likely to simplify code. */ /* Currently, objects of all +bob types are tagged with the same name: */ static char plustag_name[] = "+bob"; static void plustag_init(t_symbol *tag) { tag->s_name = plustag_name; tag->s_thing = 0; tag->s_next = 0; } /* silent if caller is empty */ int plustag_isvalid(t_symbol *tag, t_pd *caller) { if (tag->s_name == plustag_name) return (1); else if (caller) { if (strcmp(tag->s_name, plustag_name)) loud_error((caller == PLUSBOB_OWNER ? 0 : caller), "does not understand '%s' (check object connections)", tag->s_name); else loud_error((caller == PLUSBOB_OWNER ? 0 : caller), "confused..."); } return (0); } /* +bob is an object tossed around, a bobbing object. Currently, this is a wrapping for Tcl_Interp, Tcl_Obj, or a tcl variable, but the +bob interface is abstract enough to be suitable for other types of objects. The t_plusbob is kind of a virtual base. */ struct _plustype { t_plustype *tp_base; /* empty, if directly derived from t_plusbob */ t_symbol *tp_name; size_t tp_size; /* constructor is to be called explicitly, from derived constructors, or from a public wrapper. */ t_plustypefn tp_deletefn; /* destructor */ t_plustypefn tp_preservefn; t_plustypefn tp_releasefn; t_plustypefn tp_attachfn; }; static t_plustype *plustype_default = 0; t_plustype *plustype_new(t_plustype *base, t_symbol *name, size_t sz, t_plustypefn deletefn, t_plustypefn preservefn, t_plustypefn releasefn, t_plustypefn attachfn) { t_plustype *tp = getbytes(sizeof(*tp)); tp->tp_base = base; tp->tp_name = name; tp->tp_size = sz; tp->tp_deletefn = deletefn; tp->tp_preservefn = preservefn; tp->tp_releasefn = releasefn; tp->tp_attachfn = attachfn; return (tp); } static void plusbob_doattach(t_plusbob *bob, t_plusbob *parent) { if (bob->bob_parent = parent) { /* become the youngest child: */ bob->bob_prev = 0; if (bob->bob_next = parent->bob_children) { if (parent->bob_children->bob_prev) bug("plusbob_doattach 1"); parent->bob_children->bob_prev = bob; } parent->bob_children = bob; } else bug("plusbob_doattach 2"); } static void plusbob_dodetach(t_plusbob *bob) { if (bob->bob_parent) { if (bob->bob_prev) { if (bob == bob->bob_parent->bob_children) bug("plusbob_dodetach 1"); bob->bob_prev->bob_next = bob->bob_next; } if (bob->bob_next) bob->bob_next->bob_prev = bob->bob_prev; if (bob == bob->bob_parent->bob_children) bob->bob_parent->bob_children = bob->bob_next; } else bug("plusbob_dodetach 2"); } /* To be called from derived constructors. Preserving is caller's responsibility. */ t_plusbob *plusbob_create(t_plustype *tp, t_plusbob *parent) { t_plusbob *bob; if (!tp) { if (!plustype_default) plustype_default = plustype_new(0, 0, sizeof(t_plusbob), 0, 0, 0, 0); tp = plustype_default; } if (bob = getbytes(tp->tp_size)) { plustag_init(&bob->bob_tag); bob->bob_type = tp; while (tp->tp_base) tp = tp->tp_base; bob->bob_root = tp; bob->bob_owner = 0; bob->bob_refcount = 0; bob->bob_dorefcount = 1; bob->bob_children = 0; if (parent) plusbob_doattach(bob, parent); else bob->bob_parent = 0; } return (bob); } /* Should never be called, but from plusbob_release(). Calling from a derived destructor is illegal. */ static void plusbob_free(t_plusbob *bob) { t_plustype *tp; if (bob->bob_parent) plusbob_dodetach(bob); for (tp = bob->bob_type; tp; tp = tp->tp_base) if (tp->tp_deletefn) (*tp->tp_deletefn)(bob); freebytes(bob, (bob->bob_type ? bob->bob_type->tp_size : sizeof(*bob))); } void plusbob_preserve(t_plusbob *bob) { if (bob->bob_dorefcount) { t_plustype *tp; for (tp = bob->bob_type; tp; tp = tp->tp_base) if (tp->tp_preservefn) (*tp->tp_preservefn)(bob); bob->bob_refcount++; } } void plusbob_release(t_plusbob *bob) { if (bob->bob_dorefcount) { t_plustype *tp; for (tp = bob->bob_type; tp; tp = tp->tp_base) if (tp->tp_releasefn) (*tp->tp_releasefn)(bob); if (--bob->bob_refcount <= 0) { if (bob->bob_refcount == 0) plusbob_free(bob); else bug("plusbob_release"); } } } t_plusbob *plusbob_getparent(t_plusbob *bob) { return (bob->bob_parent); } /* To be called for redirection only. Bobs created as orphans are a special case, and cannot be attached later on. Likewise, changing non-orphan bobs to orphans is illegal. */ void plusbob_attach(t_plusbob *bob, t_plusbob *newparent) { if (bob->bob_parent && newparent) { t_plustype *tp; plusbob_dodetach(bob); plusbob_doattach(bob, newparent); for (tp = bob->bob_type; tp; tp = tp->tp_base) if (tp->tp_attachfn) (*tp->tp_attachfn)(bob); } else if (newparent) bug("plusbob_attach 1"); else bug("plusbob_attach 2"); } t_plusbob *plusbob_getnext(t_plusbob *bob) { return (bob->bob_next); } t_plusbob *plusbob_getchildren(t_plusbob *bob) { return (bob->bob_children); } /* Redirect all bobs to a replacement parent. Assuming replacement exists. */ void plusbob_detachchildren(t_plusbob *bob, t_plusbob *newparent) { while (bob->bob_children) plusbob_attach(bob->bob_children, newparent); } void plusbob_detachownedchildren(t_plusbob *bob, t_plusbob *newparent, t_pd *owner) { t_plusbob *child = bob->bob_children, *next; while (child) { next = child->bob_next; if (child->bob_owner == owner) plusbob_attach(child, newparent); child = next; } } void plusbob_setowner(t_plusbob *bob, t_pd *owner) { bob->bob_owner = owner; } t_pd *plusbob_getowner(t_plusbob *bob) { return (bob->bob_owner); } void outlet_plusbob(t_outlet *o, t_plusbob *bob) { outlet_symbol(o, (t_symbol *)bob); } /* silent if caller is empty */ int plustag_validtype(t_symbol *tag, t_symbol *tname, t_pd *caller) { if (tag->s_name == plustag_name) { if (((t_plusbob *)tag)->bob_type->tp_name == tname) return (1); else if (caller) { t_symbol *s = ((t_plusbob *)tag)->bob_type->tp_name; loud_error((caller == PLUSBOB_OWNER ? ((t_plusbob *)tag)->bob_owner : caller), "invalid type '%s' ('%s' expected)", (s ? s->s_name : ""), (tname ? tname->s_name : "")); } } else if (plustag_isvalid(tag, caller)) /* print the error there */ bug("plustag_validtype"); return (0); } /* silent if caller is empty */ int plustag_validroot(t_symbol *tag, t_symbol *rname, t_pd *caller) { if (tag->s_name == plustag_name) { if (((t_plusbob *)tag)->bob_root->tp_name == rname) return (1); else if (caller) { t_symbol *s = ((t_plusbob *)tag)->bob_root->tp_name; loud_error((caller == PLUSBOB_OWNER ? ((t_plusbob *)tag)->bob_owner : caller), "invalid base type '%s' ('%s' expected)", (s ? s->s_name : ""), (rname ? rname->s_name : "")); } } else if (plustag_isvalid(tag, caller)) /* print the error there */ bug("plustag_validroot"); return (0); } t_symbol *plustag_typename(t_symbol *tag, int validate, t_pd *caller) { if (!validate || tag->s_name == plustag_name) return (((t_plusbob *)tag)->bob_type->tp_name); else if (plustag_isvalid(tag, caller)) /* print the error there */ bug("plustag_typename"); return (0); } t_symbol *plustag_rootname(t_symbol *tag, int validate, t_pd *caller) { if (!validate || tag->s_name == plustag_name) return (((t_plusbob *)tag)->bob_root->tp_name); else if (plustag_isvalid(tag, caller)) /* print the error there */ bug("plustag_rootname"); return (0); } /* Plusenv (aka +env) is the base for an `environment' +bob. Environment encapsulates data common for a collection of +bobs. This is the standard way of grouping +bobs, according to a parent/children relationship. */ static t_plustype *plusenv_type = 0; static t_plusbob *plusenv_parent = 0; /* the parent of all environments */ /* To be called from derived constructors (or, LATER, plusenv's provider). */ t_plusenv *plusenv_create(t_plustype *tp, t_plusbob *parent, t_symbol *id) { t_plusenv *env = 0; if (env = (t_plusenv *)plusbob_create(tp, parent)) { if (!id) /* LATER design a public interface for bob_dorefcount */ ((t_plusbob *)env)->bob_dorefcount = 0; env->env_id = id; /* LATER rethink */ } return (env); } t_plusenv *plusenv_find(t_symbol *id, t_plusenv *defenv) { if (plusenv_parent && id) { t_plusbob *bob; for (bob = plusenv_parent->bob_children; bob; bob = bob->bob_next) if (((t_plusenv *)bob)->env_id == id) break; return ((t_plusenv *)bob); } else return (defenv); } t_symbol *plusenv_getid(t_plusenv *env) { return (env->env_id); } /* Type ignored, LATER rethink. */ t_plusbob *plusenv_getparent(t_plustype *tp) { if (!plusenv_parent) plusenv_parent = plusbob_create(0, 0); return (plusenv_parent); } t_plustype *plusenv_setup(void) { if (!plusenv_type) { plusenv_type = plustype_new(0, gensym("+env"), sizeof(t_plusenv), 0, 0, 0, 0); } return (plusenv_type); }