pure-data/src/m_class.c

/* Copyright (c) 1997-1999 Miller Puckette.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

#define PD_CLASS_DEF
#include "m_pd.h"
#include "m_imp.h"
#include "s_stuff.h"
#include "g_canvas.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef _WIN32
#include <io.h>
#endif

#include <stdarg.h>
#include <string.h>
#include <stdio.h>

#include "m_private_utils.h"

static t_symbol *class_loadsym;     /* name under which an extern is invoked */
static void pd_defaultfloat(t_pd *x, t_float f);
static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv);
t_pd pd_objectmaker;    /* factory for creating "object" boxes */
t_pd pd_canvasmaker;    /* factory for creating canvases */

static t_symbol *class_extern_dir;

#ifdef PDINSTANCE
static t_class *class_list = 0;
PERTHREAD t_pdinstance *pd_this = NULL;
t_pdinstance **pd_instances;
int pd_ninstances;
#else
t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything,
   s_signal, s__N, s__X, s_x, s_y, s_;
#endif
t_pdinstance pd_maininstance;

static t_symbol *dogensym(const char *s, t_symbol *oldsym,
    t_pdinstance *pdinstance);
void x_midi_newpdinstance( void);
void x_midi_freepdinstance( void);
void s_inter_newpdinstance( void);
void s_inter_free(t_instanceinter *inter);
void g_canvas_newpdinstance( void);
void g_canvas_freepdinstance( void);
void d_ugen_newpdinstance( void);
void d_ugen_freepdinstance( void);
void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv);

void s_stuff_newpdinstance(void)
{}

void s_stuff_freepdinstance(void)
{}

static t_pdinstance *pdinstance_init(t_pdinstance *x)
{}

static void class_addmethodtolist(t_class *c, t_methodentry **methodlist,
    int nmethod, t_gotfn fn, t_symbol *sel, unsigned char *args,
        t_pdinstance *pdinstance)
{}

#ifdef PDINSTANCE
void pd_setinstance(t_pdinstance *x)
{
    pd_this = x;
}

t_pdinstance *pd_getinstance(void)
{
    return pd_this;
}

static void pdinstance_renumber(void)
{
    int i;
    for (i = 0; i < pd_ninstances; i++)
        pd_instances[i]->pd_instanceno = i;
}

extern void text_template_init(void);
extern void garray_init(void);

t_pdinstance *pdinstance_new(void)
{
    t_pdinstance *x = (t_pdinstance *)getbytes(sizeof(t_pdinstance));
    t_class *c;
    int i;
    pd_this = x;
    s_inter_newpdinstance();
    pdinstance_init(x);
    sys_lock();
    pd_globallock();
    pd_instances = (t_pdinstance **)resizebytes(pd_instances,
        pd_ninstances * sizeof(*pd_instances),
        (pd_ninstances+1) * sizeof(*pd_instances));
    pd_instances[pd_ninstances] = x;
    for (c = class_list; c; c = c->c_next)
    {
        c->c_methods = (t_methodentry **)t_resizebytes(c->c_methods,
            pd_ninstances * sizeof(*c->c_methods),
            (pd_ninstances + 1) * sizeof(*c->c_methods));
        c->c_methods[pd_ninstances] = t_getbytes(0);
        for (i = 0; i < c->c_nmethod; i++)
            class_addmethodtolist(c, &c->c_methods[pd_ninstances], i,
                c->c_methods[0][i].me_fun,
                dogensym(c->c_methods[0][i].me_name->s_name, 0, x),
                    c->c_methods[0][i].me_arg, x);
    }
    pd_ninstances++;
    pdinstance_renumber();
    pd_bind(&glob_pdobject, gensym("pd"));
    text_template_init();
    garray_init();
    pd_globalunlock();
    sys_unlock();
    return (x);
}

void pdinstance_free(t_pdinstance *x)
{
    t_symbol *s;
    t_canvas *canvas;
    int i, instanceno;
    t_class *c;
    t_instanceinter *inter;
    pd_setinstance(x);
    sys_lock();
    pd_globallock();

    instanceno = x->pd_instanceno;
    inter = x->pd_inter;
    canvas_suspend_dsp();
    while (x->pd_canvaslist)
        pd_free((t_pd *)x->pd_canvaslist);
    while (x->pd_templatelist)
        pd_free((t_pd *)x->pd_templatelist);
    for (c = class_list; c; c = c->c_next)
    {
        if(c->c_methods[instanceno])
            freebytes(c->c_methods[instanceno],
                      c->c_nmethod * sizeof(**c->c_methods));
        c->c_methods[instanceno] = NULL;
        for (i = instanceno; i < pd_ninstances-1; i++)
            c->c_methods[i] = c->c_methods[i+1];
        c->c_methods = (t_methodentry **)t_resizebytes(c->c_methods,
            pd_ninstances * sizeof(*c->c_methods),
            (pd_ninstances - 1) * sizeof(*c->c_methods));
    }
    for (i =0; i < SYMTABHASHSIZE; i++)
    {
        while ((s = x->pd_symhash[i]))
        {
            x->pd_symhash[i] = s->s_next;
            if(s != &x->pd_s_pointer &&
               s != &x->pd_s_float &&
               s != &x->pd_s_symbol &&
               s != &x->pd_s_bang &&
               s != &x->pd_s_list &&
               s != &x->pd_s_anything &&
               s != &x->pd_s_signal &&
               s != &x->pd_s__N &&
               s != &x->pd_s__X &&
               s != &x->pd_s_x &&
               s != &x->pd_s_y &&
               s != &x->pd_s_)
            {
                freebytes((void *)s->s_name, strlen(s->s_name)+1);
                freebytes(s, sizeof(*s));
            }
        }
    }
    freebytes(x->pd_symhash, SYMTABHASHSIZE * sizeof (*x->pd_symhash));
    x_midi_freepdinstance();
    g_canvas_freepdinstance();
    d_ugen_freepdinstance();
    s_stuff_freepdinstance();
    for (i = instanceno; i < pd_ninstances-1; i++)
        pd_instances[i] = pd_instances[i+1];
    pd_instances = (t_pdinstance **)resizebytes(pd_instances,
        pd_ninstances * sizeof(*pd_instances),
        (pd_ninstances-1) * sizeof(*pd_instances));
    pd_ninstances--;
    pdinstance_renumber();
    pd_globalunlock();
    sys_unlock();
    pd_setinstance(&pd_maininstance);
    s_inter_free(inter);  /* must happen after sys_unlock() */
}

#endif /* PDINSTANCE */

/* this bootstraps the class management system (pd_objectmaker, pd_canvasmaker)
 * it has been moved from the bottom of the file up here, before the class_new() undefine
 */
void mess_init(void)
{}

static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv)
{}

static void pd_defaultbang(t_pd *x)
{}

    /* am empty list calls the 'bang' method unless it's the default
    bang method -- that might turn around and call our 'list' method
    which could be an infinite recorsion.  Fall through to calling our
    'anything' method.  That had better not turn around and call us with
    an empty list.  */
void pd_emptylist(t_pd *x)
{}

static void pd_defaultpointer(t_pd *x, t_gpointer *gp)
{}

static void pd_defaultfloat(t_pd *x, t_float f)
{}

static void pd_defaultsymbol(t_pd *x, t_symbol *s)
{}

void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv);
static void class_nosavefn(t_gobj *z, t_binbuf *b);

    /* handle "list" messages to Pds without explicit list methods defined. */
static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv)
{}

    /* for now we assume that all "gobjs" are text unless explicitly
    overridden later by calling class_setbehavior().  I'm not sure
    how to deal with Pds that aren't gobjs; shouldn't there be a
    way to check that at run time?  Perhaps the presence of a "newmethod"
    should be our cue, or perhaps the "tiny" flag.  */

    /* another matter.  This routine does two unrelated things: it creates
    a Pd class, but also adds a "new" method to create an instance of it.
    These are combined for historical reasons and for brevity in writing
    objects.  To avoid adding a "new" method send a null function pointer.
    To add additional ones, use class_addcreator below.  Some "classes", like
    "select", are actually two classes of the same name, one for the single-
    argument form, one for the multiple one; see select_setup() to find out
    how this is handled.  */

extern void text_save(t_gobj *z, t_binbuf *b);

t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod,
    size_t size, int flags, t_atomtype type1, ...)
{}

void class_free(t_class *c)
{}

void class_setfreefn(t_class *c, t_classfreefn fn)
{}

#ifdef PDINSTANCE
t_class *class_getfirst(void)
{
    return class_list;
}
#endif

    /* add a creation method, which is a function that returns a Pd object
    suitable for putting in an object box.  We presume you've got a class it
    can belong to, but this won't be used until the newmethod is actually
    called back (and the new method explicitly takes care of this.) */

void class_addcreator(t_newmethod newmethod, t_symbol *s,
    t_atomtype type1, ...)
{}

void class_addmethod(t_class *c, t_method fn, t_symbol *sel,
    t_atomtype arg1, ...)
{}

    /* Instead of these, see the "class_addfloat", etc.,  macros in m_pd.h */
void class_addbang(t_class *c, t_method fn)
{}

void class_addpointer(t_class *c, t_method fn)
{}

void class_doaddfloat(t_class *c, t_method fn)
{}

void class_addsymbol(t_class *c, t_method fn)
{}

void class_addlist(t_class *c, t_method fn)
{}

void class_addanything(t_class *c, t_method fn)
{}

void class_setwidget(t_class *c, const t_widgetbehavior *w)
{}

void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *pw)
{}

const char *class_getname(const t_class *c)
{}

const char *class_gethelpname(const t_class *c)
{}

void class_sethelpsymbol(t_class *c, t_symbol *s)
{}

const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x)
{}

void class_setdrawcommand(t_class *c)
{}

int class_isdrawcommand(const t_class *c)
{}

static void pd_floatforsignal(t_pd *x, t_float f)
{}

void class_domainsignalin(t_class *c, int onset)
{}

void class_set_extern_dir(t_symbol *s)
{}

const char *class_gethelpdir(const t_class *c)
{}

static void class_nosavefn(t_gobj *z, t_binbuf *b)
{}

void class_setsavefn(t_class *c, t_savefn f)
{}

t_savefn class_getsavefn(const t_class *c)
{}

void class_setpropertiesfn(t_class *c, t_propertiesfn f)
{}

t_propertiesfn class_getpropertiesfn(const t_class *c)
{}

/* ---------------- the symbol table ------------------------ */

static t_symbol *dogensym(const char *s, t_symbol *oldsym,
    t_pdinstance *pdinstance)
{}

t_symbol *gensym(const char *s)
{}

static t_symbol *addfileextent(t_symbol *s)
{}

#define MAXOBJDEPTH
static int tryingalready;

void canvas_popabstraction(t_canvas *x);

t_symbol* pathsearch(t_symbol *s,char* ext);
int pd_setloadingabstraction(t_symbol *sym);

    /* this routine is called when a new "object" is requested whose class Pd
    doesn't know.  Pd tries to load it as an extern, then as an abstraction. */
void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv)
{}

/* This is externally available, but note that it might later disappear; the
whole "newest" thing is a hack which needs to be redesigned. */
t_pd *pd_newest(void)
{}

    /* horribly, we need prototypes for each of the artificial function
    calls in typedmess(), to keep the compiler quiet. */
    /* Even more horribly, function pointers must be called at their
    exact type in Emscripten, including return type, which makes the
    number of cases explode. The required typedefs and dispatcher code
    are generated by m_class_dispatcher.c, which see. */

t_newgimme;
t_messgimme;
t_messgimmer;

    /* this file is generated by m_dispatch_gen.c */
#include "m_dispatch.h"

void *bang_new(t_pd *dummy);
void *pdfloat_new(t_pd *dummy, t_float f);
void *pdsymbol_new(t_pd *dummy, t_symbol *s);
void *list_new(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);

void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv)
{}

    /* convenience routine giving a stdarg interface to typedmess().  Only
    ten args supported; it seems unlikely anyone will need more since
    longer messages are likely to be programmatically generated anyway. */
void pd_vmess(t_pd *x, t_symbol *sel, const char *fmt, ...)
{}

void pd_forwardmess(t_pd *x, int argc, t_atom *argv)
{}

void nullfn(void) {}

t_gotfn getfn(const t_pd *x, t_symbol *s)
{}

t_gotfn zgetfn(const t_pd *x, t_symbol *s)
{}

void c_extern(t_externclass *cls, t_newmethod newroutine,
    t_method freeroutine, t_symbol *name, size_t size, int tiny, \
    t_atomtype arg1, ...)
{}
void c_addmess(t_method fn, t_symbol *sel, t_atomtype arg1, ...)
{}

/* provide 'class_new' fallbacks, in case a double-precision Pd attempts to
 * load a single-precision external, or vice versa
 */
#ifdef class_new
# undef class_new
#endif
t_class *
#if PD_FLOATSIZE == 32
  class_new64
#else
  class_new
#endif
   (t_symbol *s, t_newmethod newmethod, t_method freemethod,
    size_t size, int flags, t_atomtype type1, ...)
{}

/* this is privately shared with d_ugen.c */
int class_getdspflags(const t_class *c)
{}